home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2009 February / PCWFEB09.iso / Software / Resources / Browsers, Managers & Extensions / Flashgot 1.1.4 / flashgot-1.1.4.xpi / components / flashgotService.js < prev   
Text File  |  2008-11-13  |  137KB  |  4,184 lines

  1. /***** BEGIN LICENSE BLOCK *****
  2.  
  3.     FlashGot - a Firefox extension for external download managers integration
  4.     Copyright (C) 2004-2008 Giorgio Maone - g.maone@informaction.com
  5.  
  6.     This program is free software; you can redistribute it and/or modify
  7.     it under the terms of the GNU General Public License as published by
  8.     the Free Software Foundation; either version 2 of the License, or
  9.     (at your option) any later version.
  10.  
  11.     This program is distributed in the hope that it will be useful,
  12.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.     GNU General Public License for more details.
  15.  
  16.     You should have received a copy of the GNU General Public License
  17.     along with this program; if not, write to the Free Software
  18.     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  19.                              
  20. ***** END LICENSE BLOCK *****/
  21.  
  22. const CI = Components.interfaces;
  23. const CC = Components.classes;
  24. const NS_BINDING_ABORTED = 0x804b0002;
  25.  
  26.  
  27. // Utilities
  28.  
  29. function Strings(chromeName) {
  30.   this.chromeName = chromeName;
  31. }
  32.  
  33. Strings.wrap = function(s, length, sep) {
  34.   if (!sep) sep = ' ';
  35.     
  36.   function wrapPara(p) {
  37.     if (!length) length = 80;
  38.     if (p.length <= length) return p;
  39.     chunks = [];
  40.     var pos;
  41.     while (p.length > length) {
  42.       pos = p.lastIndexOf(sep, length);
  43.       if (pos < 0) pos = p.indexOf(sep, length);
  44.       if (pos < 0) break;
  45.       chunks.push(p.substring(0, pos));
  46.       p = p.substring(pos + 1);
  47.     }
  48.  
  49.     if (chunks.length) {
  50.       res  = chunks.join("\n");
  51.       if (p.length) res += "\n" + p;
  52.       return res;
  53.     } else return p;
  54.   }
  55.   if (typeof(s) != "string") s = s.toString();
  56.   var paras = s.split("\n");
  57.   
  58.   for (var j = 0; j < paras.length; j++) paras[j] = wrapPara(paras[j]);
  59.   return paras.join("\n");
  60. }
  61.  
  62. Strings.prototype = {
  63.   bundles: {},
  64.   getBundle: function(path) {
  65.     if (path in this.bundles) return this.bundles[path];
  66.     try {
  67.       return this.bundles[path] = 
  68.         CC["@mozilla.org/intl/stringbundle;1"]
  69.                   .getService(CI.nsIStringBundleService)
  70.                   .createBundle("chrome://" + this.chromeName +  "/" + path +
  71.                                 "/" + this.chromeName + ".properties");
  72.     } catch(ex) {
  73.       return this.bundles[path] = null;
  74.     }
  75.   },
  76.   
  77.  
  78.   _stringFrom: function(bundle, name, parms) {
  79.     try {
  80.       return parms ? bundle.formatStringFromName(name, parms, parms.length) : bundle.GetStringFromName(name);
  81.     } catch(ex) {
  82.       return null;
  83.     }
  84.   }
  85. ,
  86.   getString: function(name, parms) {
  87.     var s=this._stringFrom(this.getBundle("locale"), name, parms);
  88.     return s || this._stringFrom(this.getBundle("content/en-US"), name, parms) || name;
  89.   }
  90.   
  91. };
  92.  
  93. var DOMUtils = {
  94.   getDocShellFromWindow: function(window) {
  95.     try {
  96.       return window.QueryInterface(CI.nsIInterfaceRequestor)
  97.                    .getInterface(CI.nsIWebNavigation)
  98.                    .QueryInterface(CI.nsIDocShell);
  99.     } catch(e) {
  100.       return null;
  101.     }
  102.   },
  103.   getChromeWindow: function(window) {
  104.     try {
  105.       return this.getDocShellFromWindow(window)
  106.         .QueryInterface(CI.nsIDocShellTreeItem).rootTreeItem
  107.         .QueryInterface(CI.nsIInterfaceRequestor)
  108.         .getInterface(CI.nsIDOMWindow).top;
  109.     } catch(e) {
  110.       return null;
  111.     }
  112.   },
  113.   updateStyleSheet: function(sheet, enabled) {
  114.     
  115.     const sssClass = CC["@mozilla.org/content/style-sheet-service;1"];
  116.     if (!sssClass) return;
  117.       
  118.     const sss = sssClass.getService(CI.nsIStyleSheetService);
  119.     const uri = CC['@mozilla.org/network/io-service;1'].getService(CI.nsIIOService)
  120.         .newURI("data:text/css," + sheet, null, null);
  121.     if (sss.sheetRegistered(uri, sss.USER_SHEET)) {
  122.       if (!enabled) sss.unregisterSheet(uri, sss.USER_SHEET);
  123.     } else {
  124.       try {
  125.         if (enabled) sss.loadAndRegisterSheet(uri, sss.USER_SHEET);
  126.       } catch(e) {
  127.         dump("Error registering stylesheet " + uri + ": " + e + "\n"); 
  128.       }
  129.     }
  130.   },
  131.   
  132.   _wm: null,
  133.   get windowMediator() {
  134.     return this._wm || (this._wm = 
  135.         CC['@mozilla.org/appshell/window-mediator;1']
  136.                   .getService(CI.nsIWindowMediator));
  137.   },
  138.   
  139.   _winType: null,
  140.   perWinType: function(delegate) {
  141.     var wm = this.windowMediator;
  142.     var w = null;
  143.     var aa = Array.prototype.slice.call(arguments);
  144.     for each(var type in ['navigator:browser', 'emusic:window', 'Songbird:Main']) {
  145.      aa[0] = type;
  146.       w = delegate.apply(wm, aa);
  147.       if (w) {
  148.         this._winType = type;
  149.         break;
  150.       }
  151.     }
  152.     return w;
  153.   },
  154.   get mostRecentBrowserWindow() {
  155.     var res = this._winType && this.windowMediator.getMostRecentWindow(this._winType, true);
  156.     return res || this.perWinType(this.windowMediator.getMostRecentWindow, true);
  157.   },
  158.   get windowEnumerator() {
  159.     var res = this._winType && this.windowMediator.getZOrderDOMWindowEnumerator(this._winType, true);
  160.     return res || this.perWinType(this.windowMediator.getZOrderDOMWindowEnumerator, true);
  161.   }
  162. };
  163.  
  164. // *****************************************************************************
  165. // START DMS CLASSES
  166. // *****************************************************************************
  167.  
  168. const ASK_NEVER = [false, false, false];
  169.  
  170. // *** Base/Windows DMS ********************************************************
  171. function FlashGotDM(name) {
  172.   if (arguments.length > 0) {
  173.     this._init(name);
  174.   }
  175. }
  176.  
  177. FlashGotDM.init = function(service) {
  178.   FlashGotDM.dms = [];
  179.   FlashGotDM.dmtests = {};
  180.   FlashGotDM.executables = {};
  181.   FlashGotDM.deleteOnExit = [];
  182.   FlashGotDM.deleteOnUninstall = [];
  183.   FlashGotDM.initDMS(service); 
  184. };
  185.  
  186. FlashGotDM.cleanup = function(uninstalling) {
  187.   var trash = [].concat(FlashGotDM.deleteOnExit);
  188.   if (uninstalling) trash = trash.concat(FlashGotDM.deleteOnUninstall);
  189.   for each (var f in trash) {
  190.     if (f instanceof CI.nsIFile) {
  191.       try { f.remove(true); } catch(ex) {}
  192.     }
  193.   }
  194. };
  195.  
  196. FlashGotDM.prototype = {
  197.   _init: function(name) {
  198.     this.name = name;
  199.     const dms = FlashGotDM.dms;
  200.     var pos = dms.length;
  201.     if (name in dms) {
  202.       var other = dms[name];
  203.       for (var j = pos; j-- > 0;) {
  204.         if (dms[j] == other) {
  205.           pos = j;
  206.           break;
  207.         }
  208.       }
  209.     }
  210.     dms[name] = dms[pos] = this;
  211.   }
  212. ,
  213.   _service: null,
  214.   _cookieManager: null,
  215.   _exeFile: false,
  216.   _supported: null,
  217.   custom: false,
  218.   disabledLink: false,
  219.   disabledSel: false,
  220.   disabledAll: false,
  221.   exeName: "FlashGot.exe",
  222.   askPath: ASK_NEVER,
  223.   cookieSupport: true,
  224.   postSupport: false,
  225.   priority: ""
  226. ,  
  227.   _codeName: null,
  228.   get codeName() {
  229.     return this._codeName || (this._codeName = this.name.replace(/\W/g,"_"));
  230.   },
  231.   
  232.   getPref: function(name, def) {
  233.     return this.service.getPref("dmsopts." + this.codeName + "." + name, def);
  234.   },
  235.   setPref: function(name, value) {
  236.     this.service.setPref("dmsopts." + this.codeName + "." + name, value);
  237.   },
  238.   
  239.   get asciiFilter() {
  240.     return this.getPref("asciiFilter", false);
  241.   },
  242.   
  243.   get shownInContextMenu() {
  244.     return this.getPref("shownInContextMenu", false);
  245.   },
  246.   set shownInContextMenu(b) {
  247.     this.setPref("shownInContextMenu", b);
  248.     return b;
  249.   }
  250. ,
  251.   get service() {
  252.     return this._service || (this._service =
  253.       CC[SERVICE_CTRID].getService(CI.nsISupports).wrappedJSObject);
  254.   }
  255. ,
  256.   get cookieManager() {
  257.     return this._cookieManager ? this._cookieManager : this._cookieManager =
  258.       CC["@mozilla.org/cookiemanager;1"
  259.         ].getService(CI.nsICookieManager);
  260.   }
  261. ,
  262.   get exeFile() {
  263.     if (typeof(this._exeFile) == "object") return this._exeFile;
  264.     const exeName = this.exeName;
  265.     if (!exeName) return this._exeFile = null;
  266.     if (typeof(FlashGotDM.executables[exeName]) == "object") {
  267.       return this._exeFile = FlashGotDM.executables[exeName];
  268.     }
  269.     try {
  270.       var exeFile = this.service.profDir.clone();
  271.       exeFile.append(exeName);
  272.       this._exeFile = this.checkExePlatform(exeFile);
  273.       if(this._exeFile) {
  274.         FlashGotDM.deleteOnUninstall.push(this._exeFile);
  275.         if (this.createExecutable()) {
  276.           this.log(this._exeFile.path + " created");
  277.         }
  278.       }
  279.     } catch(ex) {
  280.       this._exeFile=null;
  281.       this.log("Can't init " + exeName + ":\n" + ex.message);
  282.     }
  283.     return FlashGotDM.executables[exeName] = this._exeFile;
  284.   }
  285. ,
  286.   checkExePlatform: function(exeFile) {
  287.     
  288.     var path = exeFile.path;
  289.     if (/\/.*\.exe/.test(path)) {
  290.       if (!(this.service.getPref("useWine", true) || this.name == this.service.defaultDM))
  291.         return null;
  292.       
  293.       if(!FlashGotDM.wine) {
  294.         // check for wine
  295.         var wine = CC["@mozilla.org/file/local;1"].createInstance(CI.nsILocalFile);
  296.         var winePaths = this.service.getPref("wine.paths", "/usr/bin/wine:/usr/local/bin/wine:/opt/local/bin/wine:/Applications/Darwine/Wine.bundle/Contents/bin/wine");
  297.         if (!winePaths) return null;
  298.         
  299.         for each(var winePath in winePaths.split(/[;:,]+/)) {
  300.           try {
  301.             wine.initWithPath(winePath);
  302.             if(wine.exists()) {
  303.               FlashGotDM.wine = wine;
  304.               break;
  305.             }
  306.           } catch(e) {}
  307.         }
  308.         if(!FlashGotDM.wine) return null;
  309.         FlashGotDM.wineExecutables = [];
  310.       }
  311.       FlashGotDM.wineExecutables.push(exeFile);
  312.       return exeFile;
  313.     }
  314.     if (FlashGotDMMac.isMac) return null;
  315.     return /\\.*\.sh$/i.test(path) ? null : exeFile;
  316.   }
  317. ,
  318.   get supported() {
  319.     if (typeof(this._supported) == "boolean") return this._supported;
  320.     if (this.customSupportCheck) {
  321.       return this._supported = this.customSupportCheck();
  322.     }
  323.     return this.baseSupportCheck();
  324.   },
  325.   
  326.   baseSupportCheck: function() {
  327.     if (!this.exeName) return true;
  328.     if (!this.exeFile) return false;
  329.     
  330.     var dmtest;
  331.     if (typeof(FlashGotDM.dmtests[this.exeName]) != "string") {
  332.       const dmtestFile = this.service.tmpDir.clone();
  333.       dmtestFile.append(this.exeName + ".test");
  334.       try {
  335.         if (dmtestFile.exists()) {
  336.           try { dmtestFile.remove(false); } catch(rex) {}
  337.         }
  338.         this.launchSupportTest(dmtestFile);
  339.         this.log(dmtest = this.service.readFile(dmtestFile)); 
  340.       } catch(ex) {
  341.         this.log(ex.message);
  342.         dmtest = "";
  343.       }
  344.       FlashGotDM.dmtests[this.exeName] = dmtest;
  345.     } else dmtest = FlashGotDM.dmtests[this.exeName];
  346.     return this._supported = dmtest.indexOf(this.name+"|OK")>-1;
  347.   }
  348. ,
  349.   readWinRegString: function(hkroot, hkpath, hk) {
  350.     if (!hk) hk = "";
  351.     var key, ret = null;
  352.     if ("@mozilla.org/windows-registry-key;1" in CC) {  // Firefox 1.5 or newer
  353.       key = CC["@mozilla.org/windows-registry-key;1"].createInstance(CI.nsIWindowsRegKey);
  354.       key.open(key["ROOT_KEY_" + hkroot], hkpath, key.ACCESS_READ);
  355.       ret = key.readStringValue(hk);
  356.       key.close();
  357.     } else {
  358.       hkroot = hkroot.replace(/^([A-Z])*_(A_Z).*$/, "HK$1$2"); // CURRENT_USER -> HKCU
  359.       if ("@mozilla.org/winhooks;1" in CC) {    // SeaMonkey or other older non-toolkit application
  360.         key = CC["@mozilla.org/winhooks;1"].getService(CI.nsIWindowsRegistry);
  361.       } else if ("@mozilla.org/browser/shell-service;1" in CC) {
  362.         key = CC["@mozilla.org/browser/shell-service;1"].getService(CI.nsIWindowsShellService)
  363.           && ("getRegistryEntry" in key);
  364.       }
  365.       if (key) key.getRegistryEntry(key[hkroot], hkpath, hk);
  366.     }
  367.     return ret;
  368.   }
  369. ,
  370.   launchSupportTest: function (testFile) {
  371.     this.runNative(["-o", testFile.path], true);
  372.   },
  373.   
  374.   shouldList: function() {
  375.     return this.supported;
  376.   }
  377. ,
  378.   log: function(msg) {
  379.     this.service.log(msg);
  380.   }
  381. ,
  382.   updateProgress: function(links, idx, len) {
  383.     if (!links.progress) return;
  384.     if ((idx % 100) == 0) {
  385.       if (!len) {
  386.         links.progress.update(100);
  387.         return;
  388.       }
  389.       links.progress.update(70 + 29 * idx / len);
  390.     }
  391.   }
  392. ,
  393.   isValidLink: null
  394. ,
  395.   createJobHeader: function(links, opType) {
  396.     return links.length+";" + this.name + ";" +
  397.       (this.service.getPref(this.codeName + ".quiet." + opType, false)
  398.         ? this.service.OP_QET : opType)
  399.       + ";" + links.folder + ";\n"
  400.   }
  401. ,
  402.   createJobBody: function(links) {
  403.     var jobLines = [];
  404.     var postData = links.postData || "";
  405.  
  406.     for (var j = 0, len = links.length, l; j < len; j++) {
  407.       jobLines.push((l = links[j]).href,
  408.            l.description,
  409.            this.getCookie(l, links),
  410.            postData);
  411.       this.updateProgress(links, j, len);
  412.     }
  413.     return jobLines.join("\n");
  414.   }
  415. ,
  416.   createJob: function(links, opType, extras) {
  417.     var job = this.createJobHeader(links, opType) 
  418.       + this.getReferrer(links) + "\n"
  419.       + this.createJobBody(links);
  420.       
  421.     if (typeof(links.document) == "object") {
  422.       job += "\n" + links.document.referrer + "\n" + links.document.cookie + "\n";
  423.     } else {
  424.       job += "\n\n\n";
  425.     }
  426.     if(!extras) job += "\n\n";
  427.     else {
  428.       while(extras.length < 3) extras.push('');
  429.       job += extras.join("\n");
  430.     }
  431.     var cph = this.getPref("cookiePersistence", null);
  432.     if(cph != null) job += cph;
  433.     return job;
  434.   }
  435. ,
  436.   _bgJob: true,
  437.   get bgJob() {
  438.     return this._bgJob && this.service.bgProcessing; 
  439.   }
  440. ,
  441.   download: function(links, opType) {
  442.     try {
  443.       links.folder = links.folder || (links.length > 0 ? this.selectFolder(links, opType) : "");
  444.       this.checkCookieSupport();
  445.       this.performDownload(links, opType);
  446.     } catch(ex) {
  447.       this.log(ex + "\n" + ex.stack);
  448.     } finally {
  449.       this.updateProgress(links, 0); // 100%
  450.     }
  451.   }
  452. ,
  453.   // best override point
  454.   performDownload: function(links, opType) {
  455.     this.performJob(this.createJob(links, opType));
  456.   }
  457. ,
  458.   getReferrer: function(links) {
  459.     if (links.redirProcessedBy) {
  460.       for (p in links.redirProcessedBy) {
  461.         if (this.service.getPref("redir.anonymous." + p, false)) return "";
  462.       }
  463.     }
  464.     if (!this.service.getPref("autoReferrer", true))
  465.       return this.service.getPref("fakeReferrer", "");
  466.     
  467.     var ret = links.referrer || links.document && links.document.URL || links[0] && links[0].href;
  468.     
  469.     return (ret && /^https?:*/.test(ret)) ? ret : ""; 
  470.   }
  471. ,
  472.   checkCookieSupport: function() {
  473.     this.getCookie = this.cookieSupport && !this.service.getPref("omitCookies")
  474.     ? this._getCookie
  475.     :function() { return ""; }
  476.     ;
  477.   }
  478. ,
  479.   getCookie: function() { return ""; }
  480. ,
  481.   _getCookie: function(link, links) {
  482.     if (!this.cookieSupport) return (this.getCookie = function() { return ""; })();
  483.     var host, cookies;
  484.     if ((cookies = links.cookies)) {
  485.       host = link.host;
  486.       return host && cookies[host] || "";
  487.     }
  488.     this.initCookies(links);
  489.     return this._getCookie(link, links);
  490.   },
  491.   
  492.   initCookies: function(links) {
  493.     var host, cookies, j, objCookie;
  494.     const hostCookies = {};
  495.     
  496.     var l, parts;
  497.     for (j = links.length; j-- > 0;) {
  498.       l = links[j];
  499.       parts = l.href.match(/http[s]{0,1}:\/\/([^\/]+\.[^\/]+)/i); // host?
  500.       if (parts) {
  501.         host = parts[1];
  502.         var hpos = host.indexOf("@");
  503.         if (hpos > -1) host = host.substring(hpos + 1);
  504.         hostCookies[l.host = host] = "";
  505.       } else {
  506.         l.host = null;
  507.       }
  508.     }
  509.     
  510.     var cookieHost, cookieTable, tmpCookie;
  511.     const domainCookies={};
  512.  
  513.     for (var iter = this.cookieManager.enumerator; iter.hasMoreElements();) {
  514.       if ((objCookie = iter.getNext()) instanceof CI.nsICookie) {
  515.         cookieHost = objCookie.host;
  516.         if (cookieHost.charAt(0) == ".") {
  517.           cookieHost = cookieHost.substring(1);
  518.           cookieTable = domainCookies;
  519.           if (typeof(tmpCookie=domainCookies[cookieHost]) != "string") {
  520.             tmpCookie = "";
  521.           }
  522.         } else {
  523.           if (typeof(tmpCookie=hostCookies[cookieHost])!="string") continue;
  524.           cookieTable = hostCookies;
  525.         }
  526.         cookieTable[cookieHost] = tmpCookie.concat(objCookie.name + "=" + objCookie.value + "; ");
  527.       }
  528.     }
  529.  
  530.     for (cookieHost in hostCookies) {
  531.       var dotPos;
  532.       for (host = cookieHost; (dotPos=host.indexOf('.'))>=0; ) { 
  533.         if ((tmpCookie = domainCookies[host])) {
  534.           hostCookies[cookieHost] += tmpCookie;
  535.         }
  536.         host = host.substring(dotPos+1);
  537.       }
  538.     }
  539.     
  540.     links.cookies = hostCookies;
  541.   },
  542.  
  543.   // see http://www.cookiecentral.com/faq/#3.5 and http://www.xulplanet.com/references/xpcomref/ifaces/nsICookie.html
  544.   formatNSCookie: function(cookie) {
  545.     return [
  546.       cookie.host,
  547.       cookie.isDomain ? "TRUE" : "FALSE",
  548.       cookie.path,
  549.       cookie.isSecure? "TRUE" : "FALSE",
  550.       cookie.expires || this.cookieExpires,
  551.       cookie.name,
  552.       cookie.value
  553.     ].join("\t");
  554.   },
  555.   cookieExpires: 0, // to be set once in getCookies()
  556.   createCookieFile: function() {
  557.     const cookies = [];
  558.     this.cookieExpires = new Date().getTime() + 24 * 3600 * 3650; // ten years for session cookies
  559.     
  560.     for (var cookie, iter = this.cookieManager.enumerator; iter.hasMoreElements();) {
  561.       if ((cookie = iter.getNext()) instanceof CI.nsICookie) {
  562.         cookies.push(this.formatNSCookie(cookie));
  563.       }
  564.     }
  565.     
  566.     const f = this.service.tmpDir.clone();
  567.     f.append("cookies");
  568.     f.createUnique(0, 0600);
  569.     this.service.writeFile(f, cookies.join("\n"));
  570.     return f;
  571.   }
  572. ,
  573.   createJobFile: function(job) {
  574.     const jobFile = this.service.tmpDir.clone();
  575.     jobFile.append("flashgot.fgt");
  576.     jobFile.createUnique(0, 0700);
  577.     this.service.writeFile(jobFile, job);
  578.     return jobFile;
  579.   }
  580.   _waitForNative: true,
  581.   get waitForNative() {
  582.     return this._waitForNative && this.service.bgProcessing;
  583.   }
  584. ,
  585.   performJob: function(job) {
  586.     const jobFile = this.createJobFile(job);
  587.     this.runNative([jobFile.path], this.waitForNative);
  588.   }
  589. ,
  590.   createExecutable: function() {
  591.     const exeFile = this.exeFile;
  592.     if (!exeFile) return false;
  593.     
  594.     var channel;
  595.     
  596.     const ios = CC['@mozilla.org/network/io-service;1'].getService(CI.nsIIOService);
  597.     var bis = CC['@mozilla.org/binaryinputstream;1'].createInstance(CI.nsIBinaryInputStream);
  598.     
  599.     bis.setInputStream((
  600.       channel = ios.newChannel("chrome://flashgot/content/" + this.exeName, null, null)
  601.     ).open());
  602.  
  603.     const bytesCount = channel.contentLength;
  604.     const templateImage = bis.readBytes(bytesCount);
  605.     bis.close();
  606.  
  607.     if (exeFile.exists()) {
  608.       try {
  609.         bis.setInputStream((
  610.           channel = ios.newChannelFromURI(ios.newFileURI(exeFile))
  611.         ).open());
  612.         if (channel.contentLength == bytesCount) {
  613.           try {
  614.             if (bis.readBytes(bytesCount) == templateImage) {
  615.               return false;
  616.             }
  617.           } finally {
  618.             bis.close();
  619.           }
  620.         }
  621.       } catch(ioex) {
  622.         this.log(ioex);
  623.       }
  624.     }
  625.     
  626.     var bos = null;
  627.     try {
  628.       const fos = CC["@mozilla.org/network/file-output-stream;1"].createInstance(CI.nsIFileOutputStream);
  629.       fos.init(exeFile, 0x02 | 0x08, 0700, 0);
  630.       bos = CC['@mozilla.org/binaryoutputstream;1'].createInstance(CI.nsIBinaryOutputStream);
  631.       bos.setOutputStream(fos);
  632.       bos.writeBytes(templateImage, bytesCount);
  633.       bos.close();
  634.       return true;
  635.     } catch(ioex) {
  636.       this.log("Error writing " + exeFile.path + ": " + ioex);
  637.     } finally {
  638.       if (bos) try { bos.close(); } catch(e) {}
  639.     }
  640.     return false;
  641.   }
  642. ,
  643.   runNative: function(args, blocking, exeFile) {
  644.     try {
  645.       if (typeof(exeFile) == "object"
  646.         || (exeFile = this.exeFile).exists()
  647.         || this.createExecutable()) {
  648.         const proc = CC['@mozilla.org/process/util;1'].createInstance(
  649.           CI.nsIProcess);
  650.         if (FlashGotDM.wine && FlashGotDM.wineExecutables.indexOf(exeFile) > -1) {
  651.           args.unshift(exeFile.path);
  652.           exeFile = FlashGotDM.wine;
  653.         }
  654.         proc.init(exeFile);
  655.         this.log("Running " + exeFile.path + " " + args.join(" ") + " -- " +(blocking ? "blocking" : "async") );
  656.         proc.run(blocking,args,args.length,{});
  657.         if (blocking && proc.exitValue != 0) {
  658.           this.log("Warning: native invocation of\n"
  659.             +exeFile.path
  660.             +"\nwith arguments <"
  661.             +args.join(" ")
  662.             +">\nreturned " + proc.exitValue);
  663.         }
  664.         return proc.exitValue;
  665.       } else {
  666.         this.log("Bad executable " + exeFile);
  667.       }
  668.     } catch(err) {
  669.       this.log("Error running native executable:\n" + exeFile.path + 
  670.         " " + args.join(" ") + "\n" + err.message);
  671.     }
  672.     return 0xffffffff;
  673.   }
  674. ,
  675.   getWindow: function() {
  676.     return this.service.getWindow();
  677.   }
  678. ,
  679.   selectFolder: function(links, opType) { 
  680.  
  681.  
  682.     const autoPref_FF = "browser.download.useDownloadDir";
  683.     const autoPref_Moz = "browser.download.autoDownload";
  684.     
  685.     var initialDir = null;
  686.     var downloadDir = null;
  687.     links.quickDownload = false;
  688.     
  689.     const pref = CC["@mozilla.org/preferences-service;1"].getService(CI.nsIPrefBranch);
  690.     
  691.     function findDownloadDir(prefName) {
  692.       try {
  693.         downloadDir = initialDir = pref.getComplexValue(prefName, CI.nsILocalFile);
  694.         return prefName;
  695.       } catch(ex) {
  696.         return "";
  697.       }
  698.     }
  699.     const isMulti = opType != this.service.OP_ONE;
  700.     const multiDirPref = "flashgot.multiDir";
  701.     var downloadDirPref = 
  702.                     (isMulti && findDownloadDir(multiDirPref)) ||
  703.                     findDownloadDir("browser.download.dir") ||
  704.                     findDownloadDir("browser.download.downloadDir") || 
  705.                     findDownloadDir("browser.download.defaultFolder") ||
  706.                     "browser.download.dir"; 
  707.     
  708.     if (isMulti) downloadDirPref = multiDirPref;
  709.     
  710.     try {
  711.       links.quickDownload = pref.getBoolPref(autoPref_FF);
  712.     } catch(noFFEx) {
  713.       try {
  714.         links.quickDownload = pref.getBoolPref(autoPref_Moz);
  715.       } catch(noMozEx) {}
  716.     }
  717.    
  718.     if (!this.askPath[opType]) return "";
  719.     
  720.     if (((!isMulti) || this.service.getPref("multiQuiet", false)) && 
  721.         downloadDir && downloadDir.exists() && downloadDir.isDirectory()  && 
  722.         links.quickDownload) {
  723.       return downloadDir.path;
  724.     }
  725.     
  726.     var title;
  727.     try {
  728.       var bundle = CC["@mozilla.org/intl/stringbundle;1"].getService(CI.nsIStringBundleService);
  729.       bundle = bundle.createBundle("chrome://mozapps/locale/downloads/unknownContentType.properties");
  730.       title = bundle.GetStringFromName("myDownloads");
  731.     } catch(ex) {
  732.       title="Download directory";
  733.     }
  734.     title = "FlashGot (" + this.name.replace(/[\(\)]/g, "") + ") - " + title;
  735.     
  736.     const fp = CC["@mozilla.org/filepicker;1"].createInstance(CI.nsIFilePicker);
  737.     const win = this.getWindow();
  738.     fp.init(win, title, CI.nsIFilePicker.modeGetFolder);
  739.     try {
  740.       if (initialDir &&  initialDir.exists() && initialDir.isDirectory()) {
  741.         fp.displayDirectory = initialDir;
  742.       }
  743.     } catch (ex) { this.log(ex); }
  744.     
  745.     fp.appendFilters(CI.nsIFilePicker.filterAll);
  746.  
  747.     if (fp.show()==CI.nsIFilePicker.returnOK) {
  748.       var localFile = fp.file.QueryInterface(CI.nsILocalFile);
  749.       pref.setComplexValue(downloadDirPref, CI.nsILocalFile, localFile);
  750.       var path = new String(localFile.path);
  751.       path._fgSelected = true;
  752.       return path;
  753.     }
  754.     
  755.     throw new Error("Download cancelled by user");
  756.   },
  757.   sanitizeWinArg: function(a) {
  758.     return a.replace(/([\|\(\) &\^])/g, "^$1"); 
  759.   },
  760.   supportURLList: function(links, argsTemplate) {
  761.     if (/\[[^\]]*UFILE[^\]]*\]/.test(argsTemplate) && links.length) {
  762.       // we must create a file list
  763.       var sep = this.service.isWindows ? "\r\n" : "\n";
  764.       var urlList = "";
  765.       for (j = 0; j < links.length; j++) {
  766.         urlList += links[j].href + sep;
  767.       }
  768.       links.length = 1;
  769.       return this.createJobFile(urlList).path
  770.     }
  771.     return null;
  772.   },
  773.   nativeUI: null,
  774.   hideNativeUI: function(document) {
  775.     if (!(this.nativeUI && this.getPref("hideNativeUI", true))) return;
  776.     this.service.hideNativeUI(document, this.nativeUI);
  777.   }
  778. }
  779.  
  780.  
  781.  
  782.  
  783. // *** Unix-like DMS ***********************************************************
  784. function FlashGotDMX(name,cmd,argsTemplate) {
  785.   if (arguments.length != 0) {
  786.     this._init(name);
  787.     const cmds = FlashGotDMX.prototype.unixCmds;
  788.     cmds[cmds.length] = {longName: name, shortName: cmd};
  789.     this.unixCmd = cmd;
  790.     if (argsTemplate) this.argsTemplate = argsTemplate;
  791.     this.cookieSupport =  /\[.*?(?:CFILE|COOKIE).*?\]/.test(this.argsTemplate);
  792.   }
  793.   if (FlashGotDMMac.isMac) {
  794.     this.createJobFile = FlashGotDMMac.prototype.createJobFile;
  795.   }
  796. }
  797. FlashGotDMX.prototype = new FlashGotDM();
  798. FlashGotDMX.constructor = FlashGotDMX;
  799. FlashGotDMX.prototype.exeName = "flashgot.sh";
  800. FlashGotDMX.prototype.askPath = [true, true, true];
  801. FlashGotDMX.prototype.unixCmds = [];
  802. FlashGotDMX.prototype.__defineGetter__("unixShell", function() {
  803.   var f = CC["@mozilla.org/file/local;1"].createInstance(CI.nsILocalFile);
  804.   try {
  805.     f.initWithPath("/bin/sh");
  806.     if (!f.exists()) {
  807.       this.log(f.path + " not found");
  808.       f = null;
  809.     }
  810.   } catch(ex) {
  811.     f = null;
  812.     this.log("No *X shell: " + ex.message);
  813.   }
  814.   FlashGotDMX.prototype.__defineGetter__("unixShell", function() { return f; });
  815. });
  816.  
  817. FlashGotDMX.prototype.argsTemplate = "[URL]";
  818. FlashGotDMX.prototype.launchSupportTest = function(testFile) {
  819.   const cmds = this.unixCmds;
  820.   var script="(\n";
  821.   var cmd;
  822.   for (var j = cmds.length; j-->0;) {
  823.     cmd=cmds[j];
  824.     script+=" [ -x \"`which '"+cmd.shortName+"'`\" ] && echo '"
  825.       +cmd.longName+"|OK' || echo '"+cmd.longName+"|KO'\n"; 
  826.   }
  827.   script+=") > '"+ testFile.path + "'\n"; 
  828.   this.performJob(script, true);
  829. };
  830.  
  831. FlashGotDMX.prototype.createCmdLine = function(parms) {
  832.   return this.unixCmd + " " +
  833.     this.argsTemplate.replace(/\[(.*?)(URL|REFERER|COOKIE|FOLDER|POST|UFILE)(.*?)\]/g,
  834.       function(all, before, parm, after) {
  835.           v = parms[parm]; 
  836.           return typeof(v) != "undefined" && v != null
  837.             ? before + v + after
  838.             : "";
  839.       }
  840.    ) +" &\n";
  841. };
  842. FlashGotDMX.prototype.shellEsc = function(s) {
  843.   return s ? s.replace(/([\\\*\?\[\]\$&<>\|\(\)\{\};"'`])/g,"\\$1").replace(/\s/g,"\\ ") : null;
  844. };
  845. FlashGotDMX.prototype.createJob = function(links, opType) {
  846.   const shellEsc = this.shellEsc;
  847.   // basic implementation
  848.  
  849.   const folder = shellEsc(links.folder);
  850.   const referrer = shellEsc(this.getReferrer(links));
  851.   const postData = shellEsc(links.postData);
  852.   var job = "";
  853.   var l, url;
  854.  
  855.   var urlListFile = this.supportURLList(links, this.argsTemplate);
  856.   for (var j = 0, len = links.length; j < len; j++) {
  857.     l = links[j];
  858.     url = l.href;
  859.     job += this.createCmdLine({
  860.       URL: shellEsc(url), 
  861.       REFERER: referrer, 
  862.       COOKIE: shellEsc(this.getCookie(l, links)), 
  863.       FOLDER: folder, 
  864.       POST: postData,
  865.       UFILE: shellEsc(urlListFile)
  866.     });
  867.     this.updateProgress(links, j, len);
  868.   }
  869.   return job;
  870. };
  871. FlashGotDMX.prototype.performJob = function(job, blocking) {
  872.   const jobFile = this.createJobFile("#!" + this.unixShell.path + "\n" + job);
  873.   jobFile.permissions = 0700;
  874.   var exeFile = FlashGotDMMac.isMac ? FlashGotDMMac.exeFile : jobFile;
  875.   this.runNative([],
  876.     this.waitForNative || (typeof(blocking) != "undefined" && blocking),
  877.     exeFile);
  878. };
  879. FlashGotDMX.prototype.checkExePlatform = function(exeFile) {
  880.   return this.unixShell && exeFile;
  881. };
  882. FlashGotDMX.prototype.createExecutable = function() {
  883.   return false;
  884. };
  885.  
  886.  
  887.  
  888. // *** Mac OS X DMS ************************************************************
  889. function FlashGotDMMac(name, creatorId, macAppName) {
  890.   if (arguments.length != 0) {
  891.     this._initMac(name, creatorId, macAppName);
  892.   }
  893. }
  894. FlashGotDMMac.exeFile = null;
  895. FlashGotDMMac.appleScriptFile = null;
  896. FlashGotDMMac.appleScriptName = "flashgot-mac-script";
  897. FlashGotDMMac.OSASCRIPT = "/usr/bin/osascript";
  898. FlashGotDMMac.isMac = (function() {
  899.   const f = CC["@mozilla.org/file/local;1"].createInstance(CI.nsILocalFile);
  900.   try {
  901.     f.initWithPath(FlashGotDMMac.OSASCRIPT);
  902.     return f.exists();
  903.   } catch(ex) {
  904.   }
  905.   return false;
  906. })();
  907. FlashGotDMMac.prototype = new FlashGotDM();
  908. FlashGotDMMac.constructor = FlashGotDMMac;
  909. FlashGotDMMac.prototype.exeName = "FlashGot";
  910. FlashGotDMMac.prototype.cookieSupport = false;
  911. FlashGotDMMac.prototype.macCreators = [];
  912. FlashGotDMMac.prototype._initMac = function(name, creatorId, macAppName) {
  913.   this._init(name);
  914.  
  915.   if (creatorId) {
  916.     const creators=FlashGotDMMac.prototype.macCreators;
  917.     creators[creators.length] = {name: name, id: creatorId};
  918.   }
  919.   this.macAppName = macAppName ? macAppName : name;
  920.   this.initAppleScriptBridge();
  921.   FlashGotDMMac.exeFile = this.exeFile;
  922. };
  923.  
  924. FlashGotDMMac.prototype.initAppleScriptBridge = function() {
  925.   if (FlashGotDMMac.appletScriptFile) return;
  926.   
  927.   var home = this.service.home && this.service.home.parent;
  928.   if (home) home.append("mac-flashgot");
  929.  
  930.   if (!(home && home.exists() && home.isWritable())) {
  931.     (FlashGotDMMac.appleScriptFile = this.service.tmpDir.clone())
  932.       .append(FlashGotDMMac.appleScriptName);
  933.     return;
  934.   }
  935.   (FlashGotDMMac.appleScriptFile = home.clone())
  936.     .append(FlashGotDMMac.appleScriptName);
  937.   (FlashGotDMMac.prototype._exeFile = home.clone()).append(this.exeName);
  938.   
  939.   this.log("Setting executable permissions on " + this._exeFile.path);
  940.   this._exeFile.permissions = 0700;
  941.   FlashGotDMMac.prototype.createExecutable = function() { return false; }
  942. }
  943. FlashGotDMMac.prototype.shellEsc = function(s) {
  944.   return s ? "'" + s.replace(/'/g, '"\'"') + "'" : null; 
  945. }
  946. FlashGotDMMac.prototype.createScriptLauncher = function() {
  947.   return "#!/bin/sh\n" +
  948.     "SCRIPT=" + this.shellEsc(FlashGotDMMac.appleScriptFile.path) + "\n" +
  949.     "USCRIPT=\"$SCRIPT.$$\"\n" + 
  950.     "mv \"$SCRIPT\" \"$USCRIPT\" || exit 1\n" +
  951.     "head -n 1 \"$USCRIPT\" | grep '#!' >/dev/null &&  \"$USCRIPT\" || " +
  952.     FlashGotDMMac.OSASCRIPT + " \"$USCRIPT\"";
  953. };
  954. FlashGotDMMac.prototype.checkExePlatform = function(exeFile) {
  955.   return FlashGotDMMac.isMac && exeFile || null;
  956. };
  957. FlashGotDMMac.prototype.createExecutable = function() {
  958.   
  959.   
  960.   var exeFile = this._exeFile;
  961.   if (!exeFile) return false;
  962.  
  963.   try {
  964.    var scriptLauncher = this.createScriptLauncher();
  965.    var mustCreate = true;
  966.    if (exeFile.exists()) {
  967.      if (this.service.readFile(exeFile) == scriptLauncher) {
  968.        exists = true;
  969.        if (exeFile.isExecutable()) return false;
  970.        mustCreate = false;
  971.      } else {
  972.        this.log(exeFile.path + " is corrupted or obsolete, replacing it...");
  973.        try { exeFile.remove(true); } catch(rex) {} 
  974.      }
  975.    } else {
  976.      this.log(exeFile.path + " not found, creating it...");
  977.    }
  978.    if (mustCreate) {
  979.      this.log("Creating Mac executable");
  980.      exeFile.create(0, 0700);
  981.      this.service.writeFile(exeFile, scriptLauncher);
  982.    }
  983.    this.log("Setting executable permissions on " + exeFile.path);
  984.    exeFile.permissions = 0700;
  985.    return mustCreate;
  986.   } catch(ex) {
  987.     this.log("Cannot create Mac executable: " + ex.message);
  988.   }
  989.   return false;
  990. };
  991. FlashGotDMMac.prototype.launchSupportTest = function(testFile) {
  992.   const creators = FlashGotDMMac.prototype.macCreators;
  993.   
  994.   var s = [
  995.     'global gRes',
  996.     'set gRes to ""',
  997.     'on theTest(theName, theId)',
  998.     '  set gRes to gRes & theName',
  999.     '  try',
  1000.     '    tell app "Finder" to get application file id theId',
  1001.     '    set gRes to gRes & "|OK\n"',
  1002.     '  on error',
  1003.     '    set gRes to gRes & "|KO\n"',
  1004.     '  end try',
  1005.     'end theTest'
  1006.   ];
  1007.   for (var j = creators.length; j-- > 0; ) {
  1008.     s.push('theTest("' + creators[j].name + '","' +creators[j].id + '")'); 
  1009.   }
  1010.   s.push(
  1011.     'set theFile to POSIX file "' + testFile.path + '"',
  1012.     'try',
  1013.     '  set fh to open for access theFile with write permission',
  1014.     '  write (gRes) to theFile',
  1015.     '  close access fh',
  1016.     'on error',
  1017.     '  try',
  1018.     '    close access fh',
  1019.     '  end try',
  1020.     'end try'
  1021.   );
  1022.   this.performJob(s.join("\n"), true);
  1023. };
  1024. FlashGotDMMac.prototype.createJobFile = function(job) {
  1025.   const jobFile = FlashGotDMMac.appleScriptFile;
  1026.   try {
  1027.     jobFile.remove(true);
  1028.   } catch(ex) {}
  1029.   try {
  1030.     jobFile.create(0, 0600);
  1031.     this.service.writeFile(jobFile, job, /^#/.test(job) ? null : this.service.getPref("appleScriptEncoding"));
  1032.     return jobFile;
  1033.   } catch(ex) {
  1034.     this.log("Cannot write " + (jobFile && jobFile.path) + ex.message);
  1035.   }
  1036.   return null;
  1037. }
  1038. FlashGotDMMac.prototype.performJob = function(job, blocking) {
  1039.   if (this.createJobFile(job)) 
  1040.     this.runNative([], 
  1041.         typeof(blocking) == "boolean" ? blocking : this.waitForNative, 
  1042.         this.exeFile);
  1043. };
  1044.  
  1045. FlashGotDMMac.prototype.createJob = function(links,opType) {
  1046.   const referrer = this.getReferrer(links);
  1047.   var job = "tell application \""+ this.macAppName+ "\"\n";
  1048.   for (var j = 0, len = links.length; j < len; j++) {
  1049.     job += 'GetURL "' + links[j].href + '" from "' + referrer  + "\"\n";
  1050.     this.updateProgress(links, j, len);
  1051.   }
  1052.   job += "end tell\n";
  1053.   return job;
  1054. };
  1055.  
  1056.  
  1057.  
  1058. // *** Custom DMS **************************************************************
  1059. function FlashGotDMCust(name) {
  1060.   if (arguments.length == 0 || (!name) || (!name.length)) return;
  1061.   name = name.replace(/,/g, " ");
  1062.   this._init(name);
  1063.   this.prefsBase = "custom." + this.codeName + ".";
  1064. }
  1065.  
  1066. FlashGotDMCust.init = function(service) {
  1067.   const names = service.getPref("custom", "").split(/\s*,\s*/);
  1068.   for (var j = names.length; j-->0;) {
  1069.     new FlashGotDMCust(names[j]);
  1070.   }
  1071. }
  1072.  
  1073. FlashGotDMCust.persist = function(service) {
  1074.   const dms = FlashGotDM.dms;
  1075.   const cdms = [];
  1076.   for (var j = dms.length; j-->0;) {
  1077.     if (dms[j].custom) cdms.push(dms[j].name);
  1078.   }
  1079.   service.setPref("custom", cdms.join(","));
  1080. }
  1081.  
  1082. FlashGotDMCust.prototype = new FlashGotDM();
  1083. FlashGotDMCust.constructor = FlashGotDM;
  1084.  
  1085. delete FlashGotDMCust.prototype.launchSupportTest;
  1086. delete FlashGotDMCust.prototype.exeFile;
  1087. FlashGotDMCust.prototype.PLACEHOLDERS = ["URL", "REFERER", "COOKIE", "FOLDER", "POST", "UFILE", "CFILE"];
  1088.  
  1089. FlashGotDMCust.prototype.custom = true;
  1090. FlashGotDMCust.prototype. _supported = true;
  1091. FlashGotDMCust.prototype.cookieSupport = false;
  1092. FlashGotDMCust.prototype.postSupport = true;
  1093. FlashGotDMCust.prototype.askPath = [true, true, true];
  1094.  
  1095. FlashGotDMCust.prototype.__defineGetter__("exeFile",function() {
  1096.   try {
  1097.     return this.service.prefs.getComplexValue(this.prefsBase + "exe", 
  1098.       CI.nsILocalFile);
  1099.   } catch(ex) {
  1100.     return null;
  1101.   }
  1102. });
  1103. FlashGotDMCust.prototype.__defineSetter__("exeFile",function(v) {
  1104.   try {
  1105.     if (v) {
  1106.       this.service.prefs.setComplexValue(this.prefsBase + "exe", 
  1107.           CI.nsILocalFile,v);
  1108.       return v;
  1109.     }
  1110.   } catch(ex) {
  1111.   }
  1112.   return null;
  1113. });
  1114.  
  1115. FlashGotDMCust.prototype.__defineGetter__("argsTemplate", function() {
  1116.   if (this.forcedTemplate) return this.forcedTemplate;
  1117.   var t = this.service.getPref(this.prefsBase+"args", "[URL]");
  1118.   return /['"`]/.test(t) ? this.argsTemplate = t : t;
  1119. });
  1120. FlashGotDMCust.prototype.__defineSetter__("argsTemplate",function(v) {
  1121.   if (!v) {
  1122.     v = "";
  1123.   } else {
  1124.     v = v.replace(/['"`]/g,"");
  1125.   }
  1126.   this.service.setPref(this.prefsBase + "args", v);
  1127.   this.askPath = [];
  1128.   return v;
  1129. });
  1130.  
  1131.  
  1132. FlashGotDMCust.prototype.download = function(links, opType) {
  1133.   const t = this.argsTemplate;
  1134.   this.cookieSupport = /\[.*?(?:CFILE|COOKIE).*?\]/.test(t);
  1135.   this.askPath[opType] = /\[.*?FOLDER.*?\]/.test(t);
  1136.   var exeFile = this.exeFile;
  1137.   // portable hacks
  1138.   if (exeFile && !exeFile.exists()) {
  1139.     // try changing the first part of path
  1140.     var path = exeFile.path;
  1141.     var profPath = this.service.profDir.path;
  1142.     var pos1, pos2;g
  1143.     if (path[1] == ":" && profPath[1] == ":") { 
  1144.       // easy, it's Windows, swap drive letter
  1145.       path = profPath[0] + path.substring(1);
  1146.     } else if(path.indexOf("/mount/") == 0 && profPath.indexOf("/mount/") == 0) {
  1147.       pos1 = path.indexOf("/", 7);
  1148.       pos2 = profPath.indexOf("/", 7);
  1149.       path = "/mount/" + profPath.substring(7, pos2) + path.substring(pos1); 
  1150.     } else if((pos1 = path.indexOf("/",1)) > 0 && (pos2 = profPath.indexOf("/", 1)) > 0) {
  1151.       path = profPath.substring(0, pos2) + path.substring(pos1);
  1152.     } else exeFile = null;
  1153.     if (exeFile) {
  1154.       exeFile = exeFile.clone().QueryInterface(CI.nsILocalFile).initWithPath(path);
  1155.       if (!exeFile.exists()) exeFile = null;
  1156.     }
  1157.   }
  1158.   links.exeFile= (exeFile || 
  1159.     (exeFile = this.exeFile = this.locateExeFile())) ? exeFile : null;
  1160.   FlashGotDM.prototype.download.call(this, links, opType);
  1161. };
  1162.  
  1163. FlashGotDMCust.prototype.locateExeFile = function(name) {
  1164.  
  1165.  
  1166.   if (!name) name = this.name;
  1167.   var title = this.service.getString("custom.exeFile");
  1168.   title = 'FlashGot (' + name + ') - ' + title;
  1169.   
  1170.   const fp = CC["@mozilla.org/filepicker;1"].createInstance(CI.nsIFilePicker);
  1171.   const win = this.getWindow();
  1172.   fp.init(win, title, CI.nsIFilePicker.modeOpen);
  1173.   fp.appendFilters(CI.nsIFilePicker.filterApps);
  1174.   fp.appendFilters(CI.nsIFilePicker.filterAll);
  1175.  
  1176.   if (fp.show() == CI.nsIFilePicker.returnOK) {
  1177.     var file = fp.file.QueryInterface(CI.nsILocalFile);
  1178.     if (file.exists()) {
  1179.       return file;
  1180.     }
  1181.   }
  1182.   return null;
  1183. };
  1184.  
  1185. FlashGotDMCust.prototype._addParts=function(a, s) {
  1186.   var parts=s.split(/\s+/);
  1187.   var k, p;
  1188.   for (k in parts) {
  1189.     if ((p = parts[k])) {
  1190.       a[a.length] = p;
  1191.     }
  1192.   }
  1193. };
  1194.  
  1195. FlashGotDMCust.prototype.makeArgs = function(parms) {
  1196.   const args = [];
  1197.   var t = this.argsTemplate;
  1198.   var j, v, len, s;
  1199.   
  1200.   var idx;
  1201.   
  1202.   for (var m; 
  1203.       m = t.match( /\[([\s\S]*?)(\S*)\b(URL|REFERER|COOKIE|FOLDER|POST|CFILE|UFILE)\b(\S*?)([\s\S]*?)\]/); 
  1204.       t = t.substring(idx + m[0].length) 
  1205.      ) {
  1206.  
  1207.     if ((idx = m.index) > 0) {
  1208.       this._addParts(args, t.substring(0, idx));
  1209.     }
  1210.     
  1211.     v = parms[m[3]];
  1212.     if (!v) continue;
  1213.     
  1214.     this._addParts(args, m[1]);
  1215.     args[args.length] = m[2] + v + m[4];
  1216.     this._addParts(args, m[5]);
  1217.   }
  1218.   
  1219.   if (t.length) {
  1220.     this._addParts(args, t);
  1221.   }
  1222.   return args;
  1223. };
  1224.  
  1225. FlashGotDMCust.prototype.createJob = function(links, opType) {
  1226.   return { links: links, opType: opType };
  1227. };
  1228.  
  1229. FlashGotDMCust.prototype.shellEsc = function(s) {
  1230.   return s ? '"' + s.replace(/"/g, '""') + '"' : null;
  1231. }
  1232.  
  1233. FlashGotDMCust.prototype.winEscHack = function(s) {
  1234.   // hack for bug at http://mxr.mozilla.org/seamonkey/source/xpcom/threads/nsProcessCommon.cpp#149 
  1235.   return (/[;&=]/.test(s) && !/\s/.test(s)) // "=" and ";" are command line separators on win!!!
  1236.     ? s + " " : s; // we add a space to force escaping
  1237. }
  1238.  
  1239. FlashGotDMCust.prototype.performJob = function(job) {
  1240.   const links = job.links;
  1241.   const exeFile = links.exeFile;
  1242.   if (links.length < 1 || !exeFile) return;
  1243.   
  1244.   var esc = (this.service.isWindows && this.getPref("winEscHack", true))
  1245.     ? this.winEscHack : function(s) { return s; }
  1246.   
  1247.   const folder = links.folder;
  1248.   const referrer = esc(this.getReferrer(links));
  1249.   const postData = esc(links.postData);
  1250.  
  1251.   var cookieFile;
  1252.   if (this.service.getPref("omitCookies")) {
  1253.     cookieFile = null;
  1254.   } else {
  1255.     cookieFile = this.createCookieFile().path;
  1256.   }
  1257.  
  1258.   var urlListFile = this.supportURLList(links, this.argsTemplate);
  1259.   var maxLinks = this.service.getPref(this.prefsBase + "maxLinks", 0);
  1260.   if (maxLinks > 0 && links.length > maxLinks) {
  1261.     this.log("Too many links (" + links.length + "), cutting to " 
  1262.         + this.prefsBase + "maxLinks (" + maxLinks + ")");
  1263.     links.length = maxLinks;
  1264.   }
  1265.   var l;
  1266.  
  1267.   
  1268.   for (var j = 0, len = links.length; j < len; j++) {
  1269.     l = links[j];
  1270.     this.runNative(
  1271.       this.makeArgs({
  1272.         URL: esc(l.href), 
  1273.         REFERER: referrer, 
  1274.         COOKIE: esc(this.getCookie(l, links)), 
  1275.         FOLDER: folder, 
  1276.         POST: postData,
  1277.         CFILE: cookieFile,
  1278.         UFILE: urlListFile
  1279.        }),
  1280.        false, exeFile);
  1281.     this.updateProgress(links, j, len);
  1282.   }
  1283. };
  1284. FlashGotDMCust.prototype.checkExePlatform = function(exeFile) {
  1285.   return exeFile;
  1286. };
  1287. FlashGotDMCust.prototype.createExecutable = function() {
  1288.   return false;
  1289. };
  1290. // End FlashGotDMCust.prototype
  1291.  
  1292. // *****************************************************************************
  1293. // END DMS CLASSES
  1294. // *****************************************************************************
  1295.  
  1296. // DMS initialization
  1297.  
  1298. FlashGotDM.initDMS = function(service) {
  1299.   const isWin = service.isWindows;
  1300.   var dm;
  1301.  
  1302.   new FlashGotDM("BitComet");
  1303.  
  1304.   dm = new FlashGotDM("Download Accelerator Plus");
  1305.   dm.nativeUI = "#dapctxmenu1, #dapctxmenu2";
  1306.   
  1307.   new FlashGotDM("Download Master");
  1308.   
  1309.   
  1310.   
  1311.   
  1312.   
  1313.   for each (dm in [new FlashGotDM("DTA"), new FlashGotDM("DTA (Turbo)")]) {
  1314.     dm.__defineGetter__("supported", 
  1315.       function() { 
  1316.         return  "dtaIFilterManager" in CI || "@downthemall.net/privacycontrol;1" in CC 
  1317.     });
  1318.     dm.turboDTA = /Turbo/.test(dm.name);
  1319.     dm.nativeUI = dm.turboDTA
  1320.       ? "#context-dta-savelinkt, #context-tdta, #dtaCtxTDTA, #dtaCtxSaveT"
  1321.       : "#context-dta-savelink, #context-dta, #dtaCtxDTA, #dtaCtxSave";
  1322.       
  1323.     dm.performDownload = function(links, opType) {
  1324.       if(!links.document) {
  1325.         this.log("No document found in " + links);
  1326.         return;
  1327.       }
  1328.       var w = links.browserWindow || this.service.getBrowserWindow(links.document);
  1329.       if(!(w && w.DTA_AddingFunctions && w.DTA_AddingFunctions.saveLinkArray)) {
  1330.         this.log("DTA Support problem: " + w + ", " + (w && w.DTA_AddingFunctions) + ", tb:" +
  1331.           w.gBrowser + ", wl:" + w.location + ", wo:" + w.wrappedJSObject + ", " +
  1332.             (w && w.DTA_AddingFunctions && w.DTA_AddingFunctions.saveLinkArray));
  1333.         return;
  1334.       }
  1335.       var mlSupport = w.DTA_getLinkPrintMetalink;
  1336.       var turbo = this.turboDTA;
  1337.       var cs = links.document && links.document.characterSet || "UTF-8";
  1338.       var anchors = [], images = [], l, arr;
  1339.       var hash, ml;
  1340.       var referrer = this.getReferrer(links);
  1341.       var tag;
  1342.       var single = opType == this.service.OP_ONE;
  1343.       for (var j = 0, len = links.length; j < len; j++) {
  1344.         l = links[j];
  1345.         arr = single || !(tag = l.tagName) || tag.toLowerCase() == "a" ? anchors : images;
  1346.         arr.push({ 
  1347.             url: w.DTA_URL ? new w.DTA_URL(l.href, cs) : l.href,
  1348.             description: l.description,
  1349.             ultDescription: '',
  1350.             referrer: referrer
  1351.         });
  1352.         
  1353.         if (arr == anchors && mlSupport && l.href.indexOf("#") > 0) {
  1354.           hash = l.href.match(/.*#(.*)/)[1];
  1355.           ml = mlSupport(l.href);
  1356.           if (ml) {
  1357.             arr.push({
  1358.               url: w.DTA_URL ? new w.DTA_URL(ml, cs) : ml,
  1359.               description: '[metalink] http://www.metalinker.org/',
  1360.               ultDescription: '',
  1361.               referrer: referrer,
  1362.               metalink: true
  1363.             });
  1364.           }
  1365.         }
  1366.         this.updateProgress(links, j, len);
  1367.       }
  1368.       
  1369.       w.DTA_AddingFunctions.saveLinkArray(turbo, anchors, images);
  1370.     }
  1371.   }
  1372.   
  1373.   new FlashGotDM("FlashGet");
  1374.   
  1375.   dm = new FlashGotDM("FlashGet 2");
  1376.   dm = new FlashGotDM("FlashGet 2.x");
  1377.   dm.nativeUI = "#flashgetSingle, #flashgetAll, #flashgetSep";
  1378.   
  1379.   dm = new FlashGotDM("Free Download Manager");
  1380.   dm._waitForNative=false;
  1381.   
  1382.   new FlashGotDM("FreshDownload");
  1383.   
  1384.   
  1385.   dm = new FlashGotDM("GetRight");
  1386.   dm.metalinkSupport = true;
  1387.   dm.download=function(links, opType) {
  1388.     const service = this.service;
  1389.     if (opType == service.OP_ONE && !service.getPref("GetRight.quick")) {
  1390.       opType = service.OP_SEL;
  1391.     }
  1392.     FlashGotDM.prototype.download.call(this, links, opType);
  1393.   };
  1394.   
  1395.   dm.createJob = function(links, opType) {
  1396.     const service = this.service;
  1397.     var folder = links.folder;
  1398.     if (!(folder && folder._fgSelected)) folder = false;
  1399.     
  1400.     var referrer = this.getReferrer(links);
  1401.     
  1402.     switch (opType) {
  1403.       case service.OP_ONE:
  1404.         var job = FlashGotDM.prototype.createJob.call(this, links, opType,
  1405.           this.service.getPref("GetRight.old") ? ["old"] : null
  1406.           ).replace(/; /g, ";");
  1407.         return job;
  1408.       case service.OP_SEL:
  1409.       case service.OP_ALL:
  1410.         var urlList = "";
  1411.         var referrerLine = (referrer && referrer.length > 0) ? "\r\nReferer: " + referrer + "\r\n" : "\r\n";
  1412.         var replacer = service.getPref("GetRight.replaceSpecialChars", true) ? /[^\w\.-]/g : /[\x00-\x1f\\]+/g;
  1413.         var l, k, len, decodedURL, urlParts, fileSpec, cookie;
  1414.         
  1415.         for (var j = 0; j < links.length; j++) {
  1416.           l=links[j];
  1417.           
  1418.           if (l.fname) fileSpec = l.fname;
  1419.           else if (folder) {
  1420.             fileSpec = '';
  1421.             decodedURL = unescape(l.href);
  1422.             urlParts = decodedURL.match(/\/\/.+[=\/]([^\/]+\.\w+)/);
  1423.             if (!urlParts) urlParts=l.href.match(/.*\/(.*\w+.*)/);
  1424.             if (urlParts && (fileSpec = urlParts[1])
  1425.               // && (links.length==1 ||  !/\.(php|[\w]?htm[l]?|asp|jsp|do|xml|rdf|\d+)$/i.test(fileSpec))
  1426.              ) {  
  1427.               urlParts = fileSpec.match(/(.*\.\w+).*/);
  1428.               if (urlParts) fileSpec = urlParts[1];
  1429.               fileSpec = fileSpec.replace(replacer, '_');
  1430.             } else continue;
  1431.           } else fileSpec = '';
  1432.           
  1433.           if (fileSpec) {
  1434.             if (folder) fileSpec = folder + "\\" + fileSpec;
  1435.             fileSpec = "File: " + fileSpec + "\r\n";
  1436.           }
  1437.           
  1438.           urlList+="URL: "+l.href
  1439.             +"\r\nDesc: "+l.description + "\r\n" + fileSpec;
  1440.           
  1441.             if (l.md5) {
  1442.             urlList += "MD5: " + l.md5 + "\r\n";
  1443.           }
  1444.           if (l.sha1) {
  1445.             urlList += "SHA1: " + l.sha1+ "\r\n";
  1446.           }
  1447.           if (l.metalinks) {
  1448.             for (k = 0, len = Math.min(16, l.metalinks.length); k < len; k++) {
  1449.               urlList += "Alt: " + l.metalinks[k] + "\r\n";
  1450.             }
  1451.           } else {
  1452.             urlList += referrerLine;
  1453.             if ((cookie = this.getCookie(l, links))) {
  1454.               urlList += "Cookie: " + cookie + "\r\n";
  1455.             }
  1456.           }
  1457.           this.updateProgress(links, j, len);
  1458.         }
  1459.         var file = service.tmpDir.clone();
  1460.         file.append("flashgot.grx");
  1461.         file.createUnique(0,0600);
  1462.         var charset=null;
  1463.         try {
  1464.           charset=service.getPref("GetRight.charset",
  1465.             service.prefService.QueryInterface(CI.nsIPrefBranch
  1466.             ).getComplexValue("intl.charset.default",
  1467.               CI.nsIPrefLocalizedString).data);
  1468.         } catch(ex) {}
  1469.         service.writeFile(file, urlList, charset);
  1470.         referrer = file.path;
  1471.         break;
  1472.     }
  1473.     var cmdOpts="/Q";
  1474.     if (service.getPref("GetRight.autostart",false)) { // CHECK ME!!!
  1475.       cmdOpts+="\n /AUTO";
  1476.     }
  1477.     return this.createJobHeader({ length: 0, folder: "" }, opType) +
  1478.       referrer + "\n" + cmdOpts;
  1479.   };
  1480.   dm.askPath=[false,true,true];
  1481.   
  1482.   new FlashGotDM("GigaGet");
  1483.   
  1484.   new FlashGotDM("HiDownload");
  1485.   new FlashGotDM("InstantGet");
  1486.   
  1487.   dm = new FlashGotDM("iGetter Win");
  1488.   dm.nativeUI = "#all-igetter, #igetter-link";
  1489.   dm.__defineGetter__("supported", function() {
  1490.     if (typeof(this._supported) == "boolean") return this._supported;
  1491.     if (FlashGotDMMac.isMac) return this._supported = false;
  1492.     
  1493.     this._supported = ("nsIGetterMoz" in CI);
  1494.     this.cookieSupport = false;
  1495.     if (this._supported) return true;
  1496.     this.cookieSupport = true;
  1497.     return this._supported = !!this.createExecutable();
  1498.   });
  1499.   dm.createExecutable = function() {
  1500.     var exeFile, path, key;
  1501.     
  1502.     exeFile = CC["@mozilla.org/file/local;1"].createInstance(CI.nsILocalFile);
  1503.     try {
  1504.       path = this.readWinRegString("CURRENT_USER", "Software\\iGetter")
  1505.       exeFile.initWithPath(path);
  1506.     } catch(e) {
  1507.       path = null;
  1508.     }
  1509.     if (!(path && exeFile.exists())) {
  1510.       try {
  1511.         exeFile = CC["@mozilla.org/file/directory_service;1"].getService(CI.nsIProperties)
  1512.                     .get("ProgF", CI.nsIFile);
  1513.     exeFile.append("iGetter");
  1514.     exeFile.append("iGetter.exe");
  1515.       } catch(e) {
  1516.         path = "C:\\Program Files\\iGetter\\iGetter.exe";
  1517.         try {
  1518.           exeFile.initWithPath(path);
  1519.         } catch(e2) {
  1520.           return null;
  1521.         }
  1522.       }
  1523.     }
  1524.     
  1525.     this.browser = 3;
  1526.     if ("@mozilla.org/xre/app-info;1" in Components.classes) {
  1527.       var info = Components.classes["@mozilla.org/xre/app-info;1"].getService(Components.interfaces.nsIXULAppInfo);
  1528.       if(info.name.indexOf("Firefox") > -1) this.browser = 4;
  1529.     }    
  1530.     
  1531.     return exeFile.exists() ? this._exeFile = exeFile : null;
  1532.   }
  1533.   dm.createJob = function(links, opType) {
  1534.     const cs = this.cookieSupport;
  1535.     var l;
  1536.     var job = [this.getReferrer(links)];
  1537.     for (var j=0; j < links.length; j++) {
  1538.       l = links[j];
  1539.       job.push(l.href,
  1540.         cs ? l.description + "~%iget^=" + this.getCookie(l, links)
  1541.            : l.description
  1542.       );
  1543.     }
  1544.     return job.join("\r\n") + "\r\n";
  1545.   };
  1546.   dm.performJob = function(job) {
  1547.     const file = this.createJobFile(job);
  1548.     if (this.exeFile) {
  1549.       this.runNative(['-f', file.path, '-b', this.browser])
  1550.     } else {
  1551.       CC["@presenta/iGetter"]
  1552.               .getService(CI.nsIGetterMoz)
  1553.               .NewURL(file.path);
  1554.       if (file.exists()) file.remove(0);
  1555.     }
  1556.   };
  1557.   
  1558.   new FlashGotDM("Internet Download Accelerator");
  1559.   (new FlashGotDM("Internet Download Manager")).postSupport = true;
  1560.  
  1561.   var lg2002 = new FlashGotDM("LeechGet 2002");
  1562.   var lg2004 = new FlashGotDM("LeechGet");
  1563.   lg2004._bgJob = lg2002._bgJob=false;
  1564.  
  1565.   lg2004.createJob=lg2002.createJob = function(links, opType) {
  1566.     const service=this.service;
  1567.     var referrer;
  1568.     switch (opType) {
  1569.       case service.OP_ONE:
  1570.         return FlashGotDM.prototype.createJob.call(this, links, 
  1571.             links.quickDownload ? service.OP_ONE : service.OP_SEL);
  1572.         
  1573.       case service.OP_SEL:
  1574.         var htmlDoc="<html><head><title>FlashGot selection</title></head><body>";
  1575.         var l;
  1576.         for (var j=0, len=links.length; j<len; j++) {
  1577.           l = links[j];
  1578.           var des = l.description;
  1579.           var tag = l.tagName ? l.tagName.toLowerCase() : "";
  1580.           htmlDoc = htmlDoc.concat(tag == "img"
  1581.             ? '<img src="' + l.href + '" alt="' + des
  1582.               + '" width="' + l.width + '" height="' + l.height +
  1583.               "\" />\n"
  1584.             : "<a href=\"" + l.href + "\">" + des + "</a>\n");
  1585.           this.updateProgress(links, j, len);
  1586.         }
  1587.         referrer = service.httpServer.addDoc(
  1588.           htmlDoc.concat("</body></html>")
  1589.         );
  1590.         break;
  1591.        default:
  1592.         referrer = links.document && links.document.URL || "";
  1593.         if (referrer.match(/^\s*file:/i)) { // fix for local URLs
  1594.           // we serve local URLs through built-in HTTP server...
  1595.           return this.createJob(links,service.OP_SEL);
  1596.         }
  1597.     }
  1598.     return this.createJobHeader({ length: 0, folder: "" },opType) + referrer + "\n";
  1599.   };
  1600.  
  1601.   new FlashGotDM("Net Transport");
  1602.   new FlashGotDM("Net Transport 2");
  1603.   new FlashGotDM("NetAnts");
  1604.   new FlashGotDM("Mass Downloader");
  1605.   
  1606.   dm = new FlashGotDM("Orbit");
  1607.   dm.nativeUI = "#OrbitDownloadUp, #OrbitDownload, #OrbitDownloadAll";
  1608.   
  1609.   dm = new FlashGotDM("ReGet");
  1610.   dm.postSupport = true;
  1611.   if("@reget.com/regetextension;1" in CC) {
  1612.     try {
  1613.       dm.reGetService = CC["@reget.com/regetextension;1"].createInstance(CI.IRegetDownloadFFExtension);
  1614.       if (dm.reGetService.isExtensionEnabled()) {
  1615.         dm._supported = true;
  1616.         dm.performJob = function() {};
  1617.         dm.createJob = function(links, opType) {
  1618.           const rg = this.reGetService;
  1619.           var l;
  1620.           var len = links.length;
  1621.           var ref = links.referrer;
  1622.           if (len == 1) {
  1623.             l = links[0];
  1624.             rg.setUrl(l.href);
  1625.             rg.setInfo(l.description);
  1626.             rg.setCookie(this.getCookie(l, links));
  1627.             rg.setReferer(ref);
  1628.             rg.setPostData(links.postData);
  1629.             rg.setConfirmation(true);
  1630.             rg.addDownload();
  1631.             return;
  1632.           }
  1633.           for (var j = 0; j < len; j++) {
  1634.             l = links[j];
  1635.             rg.addToMassDownloadList(
  1636.               l.href,
  1637.               ref,
  1638.               this.getCookie(l, links),
  1639.               l.description,
  1640.               "");
  1641.             this.updateProgress(links, j, len);
  1642.           }
  1643.           rg.runMassDownloadList();
  1644.         }
  1645.       }
  1646.     } catch(rgEx) {}
  1647.   }
  1648.   
  1649.   if (isWin) {
  1650.     dm = new FlashGotDMCust("Retriever");
  1651.     dm.cookieSupport = true;
  1652.     dm.askPath = ASK_NEVER;
  1653.     dm.custom = false;
  1654.     dm._supported = null;
  1655.     
  1656.     if (service.getPref(dm.prefsBase + "maxLinks", -1000) == -1000) {
  1657.       service.setPref(dm.prefsBase + "maxLinks", 10);
  1658.     }
  1659.     dm.customSupportCheck = function() {
  1660.       var wrk;
  1661.       try {
  1662.         cmd = this.readWinRegString("CLASSES_ROOT", "Retriever.Retriever.jar.HalogenWare\\shell\\Open\\command");
  1663.         this.jarPath = cmd.replace(/.*-jar "?(.*?\.jar).*/, "$1");
  1664.         this.argsTemplate = "[URL] [Referer:REFERER] [Cookie:COOKIE] [post:POST]";
  1665.         
  1666.         var exeFile = CC["@mozilla.org/file/directory_service;1"].getService(CI.nsIProperties)
  1667.           .get("WinD", CI.nsIFile);
  1668.         exeFile.append("System32");
  1669.         exeFile.append("javaw.exe");
  1670.         this.exeFile = exeFile;
  1671.         
  1672.         return true;
  1673.       } catch(e) {
  1674.         return false;
  1675.       } 
  1676.     };
  1677.     
  1678.     dm.makeArgs = function(parms) {
  1679.       return ["-jar", this.jarPath].concat(
  1680.         FlashGotDMCust.prototype.makeArgs.apply(this, arguments)
  1681.       );
  1682.     };
  1683.     
  1684.     dm = new FlashGotDMCust("DownloadStudio");
  1685.     dm.cookieSupport = true;
  1686.     dm.askPath = ASK_NEVER;
  1687.     dm.custom = false;
  1688.     dm._supported = null;
  1689.     
  1690.     dm.customSupportCheck = function() {
  1691.       var wrk;
  1692.       try {
  1693.         var path = this.readWinRegString("LOCAL_MACHINE", "SOFTWARE\\Conceiva\\DownloadStudio", "Path");
  1694.         if (!path) return false;
  1695.         var exeFile = CC["@mozilla.org/file/local;1"].createInstance(CI.nsILocalFile);
  1696.         exeFile.initWithPath(path);
  1697.         exeFile.append("DownloadStudio.exe");
  1698.         if (exeFile.exists() && exeFile.isExecutable()){
  1699.           this.exeFile = exeFile;
  1700.           this.argsTemplate = "<downloadstudio><originator>firefox</originator> " +
  1701.             "<script><add_jobs display_dialog=yes><joblist><job> [<url>URL</url>] " +
  1702.             "[<url_list_file>UFILE</url_list_file>] [<referer>REFERER</referer>] " +
  1703.             "[<post_data>POST</post_data>] [<cookie>COOKIE</cookie>] " +
  1704.             "</job></joblist></add_jobs></script></downloadstudio>";
  1705.           return true;
  1706.         }
  1707.       } catch(e) {}
  1708.       return false;
  1709.     };
  1710.     
  1711.   }
  1712.   
  1713.   const httpFtpValidator = function(url) {
  1714.     return /^(http:|ftp:)/.test(url);
  1715.   };
  1716.   dm = new FlashGotDM("Star Downloader");
  1717.   dm.cookieSupport = false;
  1718.   dm.isValidLink = httpFtpValidator;
  1719.   dm._waitForNative = false;
  1720.   
  1721.   dm = new FlashGotDM("TrueDownloader");
  1722.   dm.isValidLink = httpFtpValidator;
  1723.   dm._waitForNative = false;
  1724.   
  1725.   dm = new FlashGotDM("Thunder");
  1726.   dm.nativeUI = "#ThunderDownloadUp, #ThunderDownload, #ThunderDownloadAll";
  1727.   new FlashGotDM("Thunder (Old)");
  1728.   
  1729.   
  1730.   if (isWin) {
  1731.     dm = new FlashGotDM("WellGet");
  1732.     dm.getRelativeExe = function() {
  1733.       try {
  1734.         return this.service.prefs.getComplexValue("WellGet.path", CI.nsILocalFile);
  1735.       } catch(ex) {}
  1736.       return null;
  1737.     };
  1738.     dm.customSupportCheck = function() {
  1739.       var wellGetExe = this.getRelativeExe();
  1740.       try {
  1741.          var currentPath = wellGetExe.path;
  1742.          if(wellGetExe.exists() && wellGetExe.isExecutable()) return true;
  1743.          
  1744.          wellGetExe.initWithPath(this.service.profDir.path.substring(0,2) +
  1745.            currentPath.substring(2));
  1746.          if (wellGetExe.exists() && wellGetExe.isExecutable()) {
  1747.            if(wellGetExe.path != currentPath) {
  1748.               this.service.prefs.setComplexValue("WellGet.path",  CI.nsILocalFile, wellGetExe);
  1749.            }
  1750.            return true;
  1751.          }
  1752.          return false;
  1753.       } catch(ex) {
  1754.       }
  1755.       
  1756.       return !wellGetExe && this.baseSupportCheck();
  1757.     };
  1758.     dm.createJob = function(links, opType) {
  1759.       var wellGetExe = this.getRelativeExe();
  1760.       return FlashGotDM.prototype.createJob.call(this, links, opType, 
  1761.         wellGetExe ? [wellGetExe.path] : null);
  1762.     };
  1763.     dm.shouldList = function() { return true; }
  1764.   }
  1765.   
  1766.   dm = new FlashGotDMX("Aria", "aria", "[-r REFERER] [-d FOLDER] -g [URL]");
  1767.   dm.createJob = function(links,opType) {
  1768.     return FlashGotDMX.prototype.createJob.call(this,links,opType) + "\nsleep 4\n" + this.unixCmd+" -s &\n";
  1769.   };
  1770.   dm._waitForNative = false;
  1771.   
  1772.   dm = new FlashGotDMX("Downloader 4 X (nt)", "nt");
  1773.   dm.createJob = function(links,opType) {
  1774.     return this.unixCmd + "&\nsleep 1\n" +
  1775.       (links.folder && links.folder._fgSelected
  1776.       ? this.unixCmd + " -d '" + links.folder + "'\n"
  1777.       :"") + 
  1778.       FlashGotDMX.prototype.createJob.call(this,links,opType);
  1779.   };
  1780.   
  1781.   dm = new FlashGotDMX("Downloader 4 X", "d4x", "[--referer REFERER] [--directory FOLDER] [-a URL] [--al POST] [COOKIE]");
  1782.   dm.askPath = [false, true, true];
  1783.   dm.cookieSupport = true;
  1784.   dm.createJob = function(links, opType) {
  1785.     const service = this.service;
  1786.     const shellEsc = this.shellEsc;
  1787.     const referrer = shellEsc(this.getReferrer(links));
  1788.     const folder = links.folder._fgSelected && links.folder || null;
  1789.     const quiet = service.getPref(this.codeName + ".quiet",false);
  1790.     const len = links.length;
  1791.     var job;
  1792.     
  1793.     if (len > 0) {
  1794.         
  1795.        var urls = [];
  1796.        for (var j = 0; j < len; j++) {
  1797.          urls.push(shellEsc(links[j].href));
  1798.          this.updateProgress(links, j, len);
  1799.        }
  1800.        urls = urls.join(" ");
  1801.        
  1802.        var promptURLs_fakePost = null;
  1803.        var quietURLs_fakeCookie = null;
  1804.        
  1805.        if (quiet) {
  1806.          quietURLs_fakeCookie = urls;
  1807.          urls = null;
  1808.        } else if(len>1) {
  1809.          promptURLs_fakePost = urls;
  1810.          urls = null;
  1811.        }
  1812.        
  1813.        job = "mkdir -p $HOME/.netscape && ln -fs " + 
  1814.         shellEsc(this.createCookieFile().path) + 
  1815.         " $HOME/.netscape/cookies\n";
  1816.         
  1817.        job += this.createCmdLine({
  1818.           URL: urls, 
  1819.           REFERER: referrer,
  1820.           COOKIE: quietURLs_fakeCookie || null,
  1821.           FOLDER: folder,
  1822.           POST: promptURLs_fakePost
  1823.        });
  1824.     } else job = "";
  1825.     
  1826.     return job;
  1827.   };
  1828.   
  1829.   dm = new FlashGotDMX("GNOME Gwget","gwget");
  1830.   dm.askPath = ASK_NEVER;
  1831.   dm.createJob=function(links, opType) {
  1832.     if (opType == service.OP_ALL) {
  1833.       links.length = 1;
  1834.       links[0].href = links.document ? links.document.URL : this.getReferrer(links);
  1835.       opType = service.OP_ONE;
  1836.     }
  1837.     return FlashGotDMX.prototype.createJob.call(this, links, opType)
  1838.   } 
  1839.   
  1840.   dm = new FlashGotDMX("KDE KGet","kget");
  1841.   dm.askPath = ASK_NEVER;
  1842.   
  1843.   if (isWin) {
  1844.     new FlashGotDM("wxDownload Fast");
  1845.   } else {
  1846.     dm=new FlashGotDMX("wxDownload Fast", "wxdfast", "[-reference REFERER] [-destination FOLDER] [-list UFILE]");
  1847.     dm.askPath = ASK_NEVER;
  1848.   }
  1849.  
  1850.   dm = new FlashGotDMX("cURL","curl", '-L -O [--referer REFERER] [-b COOKIE] [-d POST] [URL]');
  1851.   dm.postSupport = true;
  1852.   dm.createJob = function(links,opType) {
  1853.     var job="[ -x \"`which 'xterm'`\" ] &&  CURL_CMD='xterm -e curl' || CURL_CMD='curl'\n";
  1854.     if (links.folder) job += "cd '" + links.folder + "'\n";
  1855.     this.unixCmd = "$CURL_CMD";
  1856.     return job + FlashGotDMX.prototype.createJob.call(this,links,opType);
  1857.   };
  1858.   
  1859.   dm = new FlashGotDMX("Wget", "wget", '-c [--directory-prefix=FOLDER] [--referer=REFERER] [--post-data=POST] [--load-cookies=CFILE] [--header=Cookie:COOKIE] [--input-file=UFILE]');
  1860.   dm.postSupport = true;
  1861.   dm.createJob=function(links,opType) {
  1862.     var job="[ -x \"`which 'xterm'`\" ] &&  WGET_CMD='xterm -e wget' || WGET_CMD='wget'\n";
  1863.     this.unixCmd = "$WGET_CMD";
  1864.     return job + FlashGotDMX.prototype.createJob.call(this,links,opType);
  1865.   };
  1866.  
  1867.   function FlashGotDMSD(version) {
  1868.     this._initMac(typeof(version) == "number" && version > 3 ? "Speed Download" : ("Speed Download " + version), "Spee");
  1869.     this.version = version;
  1870.     if (version > 2 || version == "Lite") {
  1871.       this.cookieSupport = true;
  1872.       this.postSupport = true;
  1873.     }
  1874.   };
  1875.   
  1876.   FlashGotDMSD.prototype=new FlashGotDMMac();
  1877.   FlashGotDMSD.prototype.createJob = function(links,opType) {
  1878.     var urlList = [];
  1879.     var cookieList = [];
  1880.     var l;
  1881.     for (var j=0, len = links.length; j < len; j++) {
  1882.       l = links[j];
  1883.       urlList.push(l.href);
  1884.       if (this.cookieSupport) {
  1885.         cookieList.push(this.getCookie(l, links));
  1886.       }
  1887.       this.updateProgress(links, j, len);
  1888.     }
  1889.     var job = 'tell app "' + this.macAppName + 
  1890.       '" to AddURL {"' + urlList.join('","') + '"}';
  1891.     
  1892.     if (this.postSupport) {
  1893.  
  1894.       if (links.postData) { 
  1895.         job +=' with form data "' + links.postData + '"';
  1896.       }
  1897.  
  1898.       const referer = this.getReferrer(links);
  1899.       if (referer && referer.length) {
  1900.         job += ' from "' + referer + '"';
  1901.       }
  1902.  
  1903.       if (cookieList.length) {
  1904.         job += ' with cookies {"' + cookieList.join('","') + '"}';
  1905.       }
  1906.     }
  1907.     
  1908.     return job;
  1909.   };
  1910.   
  1911.   if (service.getPref("oldSD", false)) {
  1912.     new FlashGotDMSD(2);
  1913.     new FlashGotDMSD(3);
  1914.   }
  1915.   new FlashGotDMSD(3.5);
  1916.   new FlashGotDMSD("Lite");
  1917.   
  1918.   dm = new FlashGotDMMac("Leech", "com.manytricks.Leech");
  1919.   dm.askPath = [true, true, true];
  1920.   dm.cookieSupport = dm.postSupport = true;
  1921.   dm.createJob = function(links, opType) {
  1922.     var urlList = [];
  1923.     var cookieList = [];
  1924.     var l;
  1925.     for (var j = 0, len = links.length; j < len; j++) {
  1926.       l = links[j];
  1927.       urlList.push(l.href);
  1928.       if (this.cookieSupport) {
  1929.         cookieList.push(this.getCookie(l, links).replace(/;\s*$/, ''));
  1930.       }
  1931.       this.updateProgress(links, j, len);
  1932.     }
  1933.     
  1934.     var job = 'tell app "' + this.macAppName + '" to download URLs {"'
  1935.       + urlList.join('", "') + '"}';
  1936.     if (links.postData) {
  1937.       job += ' by posting data "' + links.postData + '"';
  1938.     }
  1939.     job += ' to POSIX path "' + links.folder + '"';
  1940.     if (cookieList.length) {
  1941.       job += ' using cookies "' + cookieList.join('; ') + '; "'; 
  1942.     }
  1943.     const referer = this.getReferrer(links);
  1944.     if (referer && referer.length) {
  1945.       job += ' with referrer "' + referer + '"';
  1946.     }
  1947.     
  1948.     return job;
  1949.   }
  1950.   
  1951.   dm = new FlashGotDMMac("iGetter", "iGET");
  1952.   dm.cookieSupport = true;
  1953.   dm.createJob = function(links, opType) {
  1954.     const referrer = this.getReferrer(links);
  1955.     var l, params = [];
  1956.     for (var j = 0, len = links.length; j < len; j++) {
  1957.       l = links[j];
  1958.       params.push('{\u00ABclass ----\u00BB:"' + l.href + 
  1959.         '", \u00ABclass refe\u00BB:"' + referrer  + 
  1960.         '", \u00ABclass cook\u00BB:"' + this.getCookie(l, links) +
  1961.         '"}');
  1962.       this.updateProgress(links, j, len);
  1963.     }
  1964.     var job = "tell application \""+ this.macAppName+ 
  1965.       "\"\n\u00ABevent iGETGURL\u00BB {" +
  1966.        params.join(",") +
  1967.       "} given \u00ABclass brsg\u00BB:\"MOZB\"\n" +
  1968.       "end tell\n";
  1969.     return job;
  1970.   };
  1971.   
  1972.  
  1973.   
  1974.   
  1975.   if ("nsIDownloadManager" in CI) {
  1976.     dm = new FlashGotDM(service.getString("dm.builtIn"));
  1977.     dm._codeName = "_Built_In_";
  1978.     dm._supported = true;
  1979.     dm.priority = "zzz"; // put on the bottom of the list
  1980.     
  1981.     dm.askPath = [true, true, true];
  1982.     dm.postSupport = true;
  1983.     dm.performDownload = function(links, opType) {
  1984.       var ios = CC["@mozilla.org/network/io-service;1"].getService(CI.nsIIOService);
  1985.       const persistFlags = CI.nsIWebBrowserPersist.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION;
  1986.       const dType = CI.nsIDownloadManager.DOWNLOAD_TYPE_DOWNLOAD;
  1987.       var postData = links.postStream || null;
  1988.       var cs = links.document && links.document.characterSet || "UTF-8";
  1989.       var ref = this.getReferrer(links);
  1990.       var refURI = ref && ios.newURI(ref, cs, null) || null;
  1991.       var uri, folder, file, m;
  1992.       var persist, args;
  1993.       var now = Date.now() * 1000;
  1994.       var dm = CC["@mozilla.org/download-manager;1"].getService(CI.nsIDownloadManager);
  1995.       folder = CC["@mozilla.org/file/local;1"].createInstance(CI.nsILocalFile);
  1996.       folder.initWithPath(links.folder);
  1997.       var mozAddDownload;
  1998.       if(dm.startBatchUpdate) {
  1999.         mozAddDownload = typeof(dType) == "undefined" 
  2000.           ? function(src, dest, des, persist) { return dm.addDownload(src, dest, des, null, now, null, persist); }
  2001.           : function(src, dest, des, persist) { return dm.addDownload(dType, src, dest, des, null, null, now, null, persist); }
  2002.           ;
  2003.         dm.startBatchUpdate();
  2004.       } else {
  2005.         mozAddDownload = function(src, dest, des, persist) { return dm.addDownload(dType, src, dest, des, null, now, null, persist); };
  2006.       }
  2007.       var dl;
  2008.       for(var j = 0, len = links.length, l; j < len; j++) {
  2009.         l = links[j];
  2010.         try {
  2011.           uri = ios.newURI(l.href, cs, null);
  2012.           if(!(uri instanceof CI.nsIURL)) continue;
  2013.           file = folder.clone();
  2014.           file.append(
  2015.             l.fname ||
  2016.             unescape((uri.fileName || uri.filePath.replace(/.*?([^\/]*)\/?$/, '$1') || uri.host))
  2017.             .replace(/[\x00-\x1f\\\/\&\|\^;:]+/g, '_')
  2018.             );
  2019.           if(!this.getPref("overwrite", false)) {
  2020.             for (;;) {
  2021.               if(!file.exists()) {
  2022.                 file.create(0, 0644);
  2023.                 break;
  2024.               } else { // rename
  2025.                 m = file.leafName.match(/(.*?)(?:\((\d+)\))?(\.[^\.]+$|$)/);
  2026.                 file.leafName = m[1] + "(" + ((m[2] && parseInt(m[2]) || 0) + 1) + ")" + m[3]; 
  2027.               }
  2028.             }
  2029.           }
  2030.           persist = CC["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"].createInstance(CI.nsIWebBrowserPersist);
  2031.           persist.persistFlags = persistFlags;
  2032.           
  2033.           this.service.log("Saving " + l.href + " to " + file.path);
  2034.           
  2035.           persist.progressListener = dl = 
  2036.             mozAddDownload(uri, ios.newFileURI(file), file.leafName, persist)
  2037.               .QueryInterface(CI.nsIWebProgressListener);
  2038.           persist.saveURI(uri, null, // cachekey
  2039.                   refURI, postData, null, file);
  2040.           this.updateProgress(links, j, len);
  2041.         } catch (e) {
  2042.           this.service.log("Skipping link " + l.href + ": " + e);
  2043.         }
  2044.       }
  2045.       if(dm.endBatchUpdate) dm.endBatchUpdate();
  2046.       if(dm.flush) dm.flush();
  2047.       
  2048.       if(this.getPref("showDM", true)) {
  2049.         try { // SeaMonkey
  2050.           dm.open(links.browserWindow, dl);
  2051.         } catch(notSeamonkey) {
  2052.           
  2053.           const DMBRANCH = "browser.download.manager.";
  2054.           var prefs = this.service.prefService.getBranch(DMBRANCH);
  2055.           try {
  2056.             if (!(prefs.getBoolPref("showWhenStarting") && prefs.getBoolPref("useWindow")))
  2057.               return;
  2058.           } catch(noPref) {
  2059.             return;
  2060.           }
  2061.           
  2062.           try { // 1.8 (Firefox)
  2063.             links.browserWindow.document.getElementById("Tools:Downloads").doCommand();
  2064.             return;
  2065.           } catch(e) {}
  2066.           
  2067.           try { // 1.9 (Toolkit)
  2068.              // http://mxr.mozilla.org/seamonkey/source/toolkit/components/downloads/src/nsDownloadProxy.h#94
  2069.              var dmui = CC["@mozilla.org/download-manager-ui;1"].getService(CI.nsIDownloadManagerUI);
  2070.              var focus = false;
  2071.              try {
  2072.                focus = prefs.getBoolPref("focusWhenStarting");
  2073.              } catch(noPref) {}
  2074.              if (dmui.visible && !focus) {
  2075.                dmui.getAttention();
  2076.                return;
  2077.              }
  2078.              dmui.show(null, dl);
  2079.           } catch(e) {
  2080.             this.log(e);
  2081.           }
  2082.         }
  2083.       }
  2084.     };
  2085.   }
  2086.   
  2087.   FlashGotDMCust.init(service);
  2088.   
  2089.   service.sortDMS();
  2090.   
  2091.   dm = null;
  2092. };
  2093.  
  2094. // *****************************************************************************
  2095. // HTTP interceptor (nsIURIContentListener + http-on-modify-request observer)
  2096. // *****************************************************************************
  2097.  
  2098. function HttpInterceptor(service) {
  2099.   this.service = service;
  2100.  
  2101.   CC["@mozilla.org/uriloader;1"].getService(
  2102.     CI.nsIURILoader).registerContentListener(this);
  2103. }
  2104.  
  2105. HttpInterceptor.prototype = {
  2106.   service: null,
  2107.   
  2108.   autoStart: false,
  2109.   interceptAll: true,
  2110.   bypassAutoStart: false,
  2111.   forceAutoStart: false,
  2112.   
  2113.   lastPost: null, // last uploadChannel
  2114.  
  2115.   interfaces: [
  2116.     CI.nsIURIContentListener,
  2117.     CI.nsIObserver, 
  2118.     CI.nsISupportsWeakReference,
  2119.     CI.nsISupports
  2120.   ],
  2121.   
  2122.   QueryInterface: function(iid) {
  2123.      xpcom_checkInterfaces(iid, this.interfaces, Components.results.NS_ERROR_NO_INTERFACE);
  2124.      return this;
  2125.   },
  2126.   
  2127.   setup: function() { // profile initialization
  2128.     this.autoStart = this.service.getPref("autoStart", false);
  2129.     this.interceptAll = this.service.getPref("interceptAll", true);
  2130.   },
  2131.   
  2132.   dispose: function() {
  2133.     CC["@mozilla.org/uriloader;1"].getService(
  2134.         CI.nsIURILoader).unRegisterContentListener(this);
  2135.   },
  2136.   
  2137.   log: function(msg) {
  2138.     this.service.log(msg);
  2139.   },
  2140.   
  2141.   _shouldIntercept: function(contentType) {
  2142.     // dump("FG: _shouldIntercept("+contentType+")\n");
  2143.     if (this.bypassAutoStart) return false;
  2144.     const service = this.service;
  2145.     if (!(service.DMS && service.DMS.found)) return false;
  2146.     if (this.forceAutoStart) return true;
  2147.     
  2148.     if (!this.autoStart) return false;
  2149.     
  2150.     if (this.interceptAll &&
  2151.       !/\bxpinstall|text|xml|vnd\.mozilla|multipart\/x-mixed-replace\b/.test(contentType)) {
  2152.       return true;
  2153.     }
  2154.  
  2155.     if (contentType == "application/x-unknown-content-type" || /\b(?:xml|rss|javascript|json)\b/.test(contentType)) return false;
  2156.     var ms = MediaSniffer.mimeService;
  2157.     return service.extensions.some(function(e) {
  2158.       try { return contentType == ms.getTypeFromExtensions(e); } catch(e) { return false; }
  2159.     });
  2160.   }
  2161.   _willHandle: function(url, contentType) {
  2162.     if (!/^(http|https|ftp|sftp|rtsp|mms):/i.test(url) ) {
  2163.       if ((/^\s*javascript/i).test(url)) this.log("JavaScript url intercepted: "+url);
  2164.       return false;
  2165.     }
  2166.     return true;
  2167.   }
  2168. ,
  2169.   extractPostData: function(channel, res) {
  2170.     res = res || {};
  2171.     res.postData = res.postStream = null;
  2172.     if (channel instanceof CI.nsIUploadChannel &&
  2173.        channel.uploadStream instanceof CI.nsISeekableStream) {
  2174.       this.log("Extracting post data...");
  2175.       try {
  2176.         res.postStream = channel.uploadStream;
  2177.         res.postStream.seek(0, 0);
  2178.         const sis=CC['@mozilla.org/scriptableinputstream;1'].createInstance(CI.nsIScriptableInputStream);
  2179.         sis.init(res.postStream);
  2180.         var postData  = sis.read(sis.available());
  2181.         res.postStream.seek(0, 0);
  2182.         
  2183.         // buffered persistent copy 
  2184.         res.postStream = CC["@mozilla.org/io/string-input-stream;1"].createInstance(CI.nsIStringInputStream);
  2185.         res.postStream.setData(postData, postData.length);
  2186.         if (res.postStream instanceof CI.nsISeekableStream) res.postStream.seek(0, 0);
  2187.         
  2188.         // remove headers
  2189.         postData = postData.replace(/\s+$/,'').split(/[\r\n]+/)
  2190.         res.postData = postData[postData.length - 1];
  2191.       } catch(ex) {
  2192.         this.log(ex.message);
  2193.       } finally {
  2194.          sis.close();
  2195.       }
  2196.     }
  2197.     return res;
  2198.   },
  2199.   /* nsIURIContentListener */
  2200.   
  2201.   canHandleContent: function(contentType, isContentPreferred, desiredContentType) {
  2202.     // dump("FG: canHandleContent "+contentType+")\n");
  2203.     return this._shouldIntercept(contentType);
  2204.   }
  2205. ,
  2206.   lastRequest: null,
  2207.   doContent: function(contentType, isContentPreferred, channel, contentHandler) {
  2208.  
  2209.     channel.QueryInterface(CI.nsIChannel);
  2210.     const uri = channel.URI;
  2211.     // dump("FG: doContent " +contentType + " " + uri.spec + "\n");
  2212.     if (!this._willHandle(uri.spec, contentType)) {
  2213.       throw new Error("FlashGot not interested in " + contentType + " from " + uri.spec);
  2214.     }
  2215.     
  2216.     this.log("Intercepting download...");
  2217.  
  2218.     const pathParts=uri.path.split(/\//);
  2219.     var links = [ {
  2220.      href: channel.URI.spec, 
  2221.      description: pathParts[pathParts.length-1]
  2222.     } ];
  2223.     
  2224.     
  2225.     
  2226.     
  2227.     if (channel instanceof CI.nsIHttpChannel) {
  2228.       links.referrer = channel.referrer && channel.referrer.spec || "";
  2229.       this.extractPostData(channel, links);
  2230.     }
  2231.     
  2232.     try {
  2233.         links.document = channel.notificationCallbacks.QueryInterface(
  2234.             CI.nsIInterfaceRequestor).getInterface(
  2235.             CI.nsIDOMWindow).document;
  2236.         links.browserWindow = DOMUtils.getChromeWindow(links.document.defaultView.top);
  2237.         if (links.browserWindow.wrappedJSObject) links.browserWindow = links.browserWindow.wrappedJSObject;
  2238.       } catch(e) {
  2239.         this.log("Can't set referrer document for " + links[0].href + " from " + links.referrer);
  2240.     }
  2241.     
  2242.     var firstAttempt;
  2243.     if (contentHandler) {
  2244.       this.lastRequest = null;
  2245.       firstAttempt = true;
  2246.       this.forceAutoStart = false;
  2247.     } else {
  2248.       var requestLines = [ channel.requestMethod, links[0].href, links.referrer || "", links.postData || ""].join("\n\n");
  2249.       firstAttempt = this.lastRequest != requestLines;
  2250.       this.lastRequest = requestLines;
  2251.     }
  2252.     
  2253.     if (firstAttempt) {
  2254.        var self = this;
  2255.        this.service._delay(function() {
  2256.           self.forceAutoStart = false;
  2257.           if(self.service.download(links))
  2258.             self.log("...interception done!");
  2259.         }, 10);
  2260.     } else {
  2261.       // dump("Second attempt, skipping.\n");
  2262.       this.lastRequest = null;
  2263.       this.forceAutoStart = false;
  2264.     }
  2265.     
  2266.     if (!channel.isPending()) { 
  2267.       try {
  2268.         channel.requestMethod = "HEAD";
  2269.         channel.loadFlags = CI.nsIChannel.LOAD_RETARGETED | CI.nsIChannel.LOAD_RETARGETED_DOCUMENT_URI | CI.nsICachingChannel.LOAD_ONLY_FROM_CACHE;
  2270.       } catch(e) {}
  2271.     }
  2272.     channel.cancel(NS_BINDING_ABORTED); 
  2273.  
  2274.     this.log("Original request cancelled.");
  2275.     
  2276.     return true;
  2277.   },
  2278.   contentHandler: {
  2279.       onStartRequest: function(request, context) { 
  2280.         throw "cancelled"; 
  2281.       }, 
  2282.       onStopRequest: function() {}, 
  2283.       onDataAvailable: function() {}
  2284.    }
  2285. ,
  2286.   isPreferred: function(contentType, desiredContentType) {
  2287.     // dump("FG: isPreferred("+contentType+","+desiredContentType+")\n");
  2288.     return this._shouldIntercept(contentType);
  2289.   }
  2290. ,
  2291.   onStartURIOpen: function(uri) {
  2292.     // dump("FG: onStartURIOpen "+ uri + (uri && uri.spec) + "\n");
  2293.     return false;
  2294.   }
  2295. ,
  2296.   // http-on-modify-request Observer 
  2297.   observe: function(channel, topic, data) {
  2298.     if (channel instanceof CI.nsIHttpChannel) {
  2299.       
  2300.       if (channel instanceof CI.nsIUploadChannel) {
  2301.         this.lastPost = channel;
  2302.       }
  2303.       if (this.forceAutoStart) {
  2304.         this.doContent("flashgot/forced", true, channel, null);
  2305.         return;
  2306.       }
  2307.       
  2308.       
  2309.     }
  2310.   }
  2311. }
  2312.  
  2313. var MediaSniffer = {
  2314.   interfaces: [
  2315.     CI.nsIObserver, 
  2316.     CI.nsISupportsWeakReference,
  2317.     CI.nsISupports,
  2318.     CI.nsIWebProgressListener
  2319.   ],
  2320.   
  2321.   QueryInterface: function(iid) {
  2322.      xpcom_checkInterfaces(iid, this.interfaces, Components.results.NS_ERROR_NO_INTERFACE);
  2323.      return this;
  2324.   },
  2325.   fg: null,
  2326.   
  2327.   debug: false,
  2328.   // http-on-examine-response Observer
  2329.   
  2330.   mimeService: CC['@mozilla.org/uriloader/external-helper-app-service;1']
  2331.                      .getService(CI.nsIMIMEService),
  2332.   mediaTypesRx: /\b(?:audio|video|smil)\b/i,
  2333.   mediaMap: {
  2334.     "asx": "video/x-ms-asx", // fake, see "media" processor
  2335.     "flv": "video/flv", // flv is not mapped by MimeService
  2336.     "mp3": "audio/mp3", // MimeService chokes on this
  2337.     "mp4": "video/mp4" // just to be sure :)
  2338.     
  2339.   },
  2340.   get inverseMediaMap() {
  2341.     var m = {};
  2342.     for (var p in this.mediaMap) m[this.mediaMap[p]] = p;
  2343.     delete this.inverseMediaMap;
  2344.     return this.inverseMediaMap = m;
  2345.   },
  2346.   
  2347.   sniffType: function(channel, forcedContentType) {
  2348.     var path;
  2349.     if (channel instanceof CI.nsIHttpChannel) {
  2350.       try {
  2351.         path = channel.getResponseHeader("content-disposition");
  2352.       } catch(e) {}
  2353.      }
  2354.     if (!path) path = channel.URI.path;
  2355.     path = path.replace(/#[\s\S]*/, '');
  2356.     if (path.length > 1024) path = path.substring(0, 1024);
  2357.     const extFinder = /([^"\/]+\.(\w{2,5}))(?=[\?&]|$)/g;
  2358.     extFinder.lastIndex = 0;
  2359.     var m, ext, contentType, ms;
  2360.     while((m = extFinder.exec(path))) {
  2361.       ext = m[2];
  2362.       if ((contentType = this.mediaMap[ext])) break;
  2363.       try {
  2364.         contentType = this.mimeService.getTypeFromExtension(ext);
  2365.         if (this.mediaTypesRx.test(contentType)) break;
  2366.       } catch(e) {}
  2367.     }
  2368.     
  2369.     var fname;
  2370.     if (forcedContentType && !(forcedContentType == "video/x-ms-asf" && contentType == "video/x-ms-asx")) {
  2371.       var fname = m && m[1];
  2372.       if (!fname) {
  2373.         fname = (channel.URI instanceof CI.nsIURL) && channel.URI.fileName || path.replace(/.*\//g, '');
  2374.         try {
  2375.           fname += "." + (this.inverseMediaMap[forcedContentType] || this.mimeService.getPrimaryExtension(forcedContentType, '')); 
  2376.         } catch(ex) {}
  2377.       }
  2378.       return { fname: fname, contentType: forcedContentType };
  2379.     }
  2380.     
  2381.     return m && { fname: m[1], contentType: contentType };
  2382.   },
  2383.   
  2384.   observe: function(channel, topic, data) {
  2385.     if (channel instanceof CI.nsIChannel) {
  2386.       try {
  2387.         var contentType = channel.contentType;
  2388.         if (!contentType || /\b(?:x?html|image|css|javascript|flash)\b/i.test(contentType)) return;
  2389.         
  2390.         if (this.debug) dump("Examining " + channel.URI.spec + " (" + contentType + ")\n");
  2391.         
  2392.         var typeInfo = null;
  2393.         if (this.mediaTypesRx.test(contentType) || (typeInfo = this.sniffType(channel))) {
  2394.  
  2395.           var win = (channel.notificationCallbacks || channel.loadGroup.notificationCallbacks)
  2396.             .QueryInterface(
  2397.               CI.nsIInterfaceRequestor).getInterface(
  2398.               CI.nsIDOMWindow).top;
  2399.  
  2400.           if (this.debug) dump("Media Window: " + win + " - " + win.location.href + " -- " + contentType + "\n");
  2401.           var media = win._flashgotMedia || (win._flashgotMedia = []);
  2402.           var url = channel.URI.spec;
  2403.           var map = media._map || (media._map = {});
  2404.           
  2405.           if (!(url in map)) {
  2406.             // asf content type can also refer to an asx, we need to check the file name to decide
  2407.             if (!typeInfo) {
  2408.               typeInfo = this.sniffType(channel, contentType)
  2409.             }
  2410.             
  2411.             contentType = typeInfo.contentType;
  2412.             
  2413.             var tip = url.match(/[^\/]*$/)[0] || '';
  2414.             if (tip) {
  2415.                 tip = contentType + ": " + tip;
  2416.                 if (tip.length > 60) {
  2417.                     tip = tip.substring(0, 29) + "..." + tip.slice(-28); 
  2418.                 }
  2419.             } else tip = contentType;
  2420.             
  2421.             var title = win.document.title || '';
  2422.             const unicode = this.fg.getPref("media.unicode");
  2423.             if (title) {
  2424.               // remove site name from title
  2425.               title = title.replace(new RegExp("\\b(?:" +
  2426.                   (win.location.host || '').split(".").filter(function(s) { return s }).join("|")  + ")\\b", 'ig'), '')
  2427.                   .replace(/https?:\/{2}/gi, '').replace(unicode ? /^[^\w\u0080-\uffff]*(.*?)[^\w\u0080-\uffff]*$/g : /^\W*(.*?)\W*$/g, '$1').replace(unicode ? /[\u0000-\u0020]+/g : /\W+/g, '_')
  2428.             }
  2429.             
  2430.             var fname = title && this.fg.getPref("media.guessName", true) && (title + "_" + typeInfo.fname)
  2431.               .replace(unicode ? /[^\w\.\u0080-\uffff]+/g : /[^\w\.]+/g, '_').replace(/_get_video\b/, '')
  2432.                         || '';
  2433.             var href =  (fname && this.fg.getPref("media.forceNameHack", true) && url.indexOf("#") == -1) ? url + "#/" + encodeURIComponent(fname) : url;
  2434.             
  2435.             media.push (map[url] = {
  2436.               href: href,
  2437.               referrer: (channel instanceof CI.nsIHttpChannel) && channel.referrer && channel.referrer.spec,
  2438.               description: title + " (" + contentType + ")",
  2439.               contentType: contentType,
  2440.               tip: tip,
  2441.               fname: fname
  2442.             });
  2443.           }
  2444.  
  2445.           var bw = DOMUtils.mostRecentBrowserWindow;
  2446.           if (bw && bw.gFlashGot) bw.gFlashGot.updateMediaUI();
  2447.           
  2448.           if (this.debug) dump(win._flashgotMedia && win._flashgotMedia.toSource() + "\n");
  2449.         }
  2450.       } catch(e) {
  2451.         var msg = topic + " " + e.toString();
  2452.         if (channel) {
  2453.           msg += " -- " + channel.URI.spec;
  2454.           try {
  2455.             msg += ", " + channel.contentType;
  2456.           } catch(e1) {}
  2457.         }
  2458.         dump(msg + "\n");
  2459.       }
  2460.     }
  2461.   },
  2462.   
  2463.   onStateChange: function(wp, channel, stateFlag) {
  2464.     // here we wait STATE_STOP of cached channels
  2465.     if ((stateFlag & 16) && (channel instanceof CI.nsICachingChannel))
  2466.       this.observe(channel, "http-cached-stop", null);
  2467.   }
  2468.   /*
  2469.   ,
  2470.   onLinkIconAvailable: function() {},
  2471.   onLocationChange: function() {},
  2472.   onStatusChange: function() {},
  2473.   onSecurityChange: function() {}, 
  2474.   onProgressChange: function() {}
  2475.   */
  2476. }
  2477.  
  2478.  
  2479. // *****************************************************************************
  2480. // XPCOM Service
  2481. // *****************************************************************************
  2482.  
  2483. const SHUTDOWN = "profile-before-change";
  2484. const STARTUP = "profile-after-change";
  2485.  
  2486. function FlashGotService() {
  2487.   
  2488.   const os = CC['@mozilla.org/observer-service;1'].getService(CI.nsIObserverService);
  2489.   
  2490.   os.addObserver(this, SHUTDOWN, false);
  2491.   os.addObserver(this, "xpcom-shutdown", false);
  2492.   os.addObserver(this, STARTUP, false);
  2493.   
  2494.   this.interceptor = new HttpInterceptor(this);
  2495.   os.addObserver(this.interceptor, "http-on-modify-request", true);
  2496.   MediaSniffer.fg = this;
  2497.   os.addObserver(MediaSniffer, "http-on-examine-response", true);
  2498.   const dls = CC['@mozilla.org/docloaderservice;1'].getService(CI.nsIWebProgress);
  2499.   dls.addProgressListener(MediaSniffer, CI.nsIWebProgress.NOTIFY_STATE_NETWORK);
  2500. }
  2501.  
  2502. FlashGotService.prototype = {
  2503.   OP_ONE: 0, 
  2504.   OP_SEL: 1,
  2505.   OP_ALL: 2,
  2506.   OP_QET: 3
  2507. ,
  2508.   VERSION: "1.1.4"
  2509. ,
  2510.   domUtils: DOMUtils,
  2511.   get wrappedJSObject() {
  2512.     return this;
  2513.   }
  2514. ,
  2515.   unregister: function() {
  2516.     try {
  2517.       const os = CC['@mozilla.org/observer-service;1'].getService(
  2518.       CI.nsIObserverService);
  2519.       os.removeObserver(this, "em-action-requested");
  2520.       os.removeObserver(this, SHUTDOWN);
  2521.       os.removeObserver(this, "xpcom-shutdown");
  2522.       os.removeObserver(this, STARTUP);
  2523.       os.removeObserver(this.interceptor, "http-on-modify-request");
  2524.       
  2525.       this.interceptor.dispose();
  2526.       this.interceptor = null;
  2527.       os.removeObserver(MediaSniffer, "http-on-examine-response");
  2528.       const dls = CC['@mozilla.org/docloaderservice;1'].getService(CI.nsIWebProgress);
  2529.       dls.removeProgressListener(MediaSniffer);
  2530.       
  2531.     } catch(ex) {
  2532.       this.log("Error unregistering service as observer: "+ex);
  2533.     }
  2534.   }
  2535. ,
  2536.   QueryInterface: function(iid) {
  2537.      xpcom_checkInterfaces(iid, SERVICE_IIDS, Components.results.NS_ERROR_NO_INTERFACE);
  2538.      return this;
  2539.   }
  2540. ,
  2541.   /* nsIObserver */  
  2542.   observe: function(subject, topic, data) {
  2543.     if (subject == this.prefs) {
  2544.       this.syncPrefs(data);
  2545.     } else {
  2546.       switch (topic) {
  2547.         case "xpcom-shutdown":
  2548.           this.unregister();
  2549.           break;
  2550.         case SHUTDOWN: 
  2551.           this.cleanup();
  2552.           break;
  2553.         case STARTUP:
  2554.           this.initGlobals();
  2555.           this.interceptor.setup();
  2556.           const osvr = CC['@mozilla.org/observer-service;1'].getService(CI.nsIObserverService);
  2557.           osvr.addObserver(this, "em-action-requested", false);
  2558.           break;
  2559.         case "em-action-requested":
  2560.           if ((subject instanceof CI.nsIUpdateItem)
  2561.               && subject.id == EXTENSION_ID) {
  2562.             if (data == "item-uninstalled") {
  2563.               this.uninstalling = true;
  2564.             } else if (data == "item-enabled" || data == "item-cancel-action") {
  2565.               this.uninstalling = false;
  2566.             }
  2567.           }
  2568.         break;
  2569.       }
  2570.     }
  2571.   },
  2572.   uninstalling: false
  2573. ,
  2574.   syncPrefs: function(name) {
  2575.     this.logEnabled = this.getPref("logEnabled", true);
  2576.     if (name) {
  2577.       switch (name) {
  2578.         case "hide-icons":
  2579.           var w;
  2580.           for (var wins = this.windowMediator.getEnumerator(null); wins.hasMoreElements();) {
  2581.              w=wins.getNext();
  2582.              if (typeof(w.gFlashGot)=="object" && w.gFlashGot.toggleMainMenuIcon) {
  2583.                w.gFlashGot.toggleMainMenuIcon();
  2584.              }
  2585.           }
  2586.         break;
  2587.         
  2588.         case "autoStart":
  2589.         case "interceptAll":
  2590.           this.interceptor[name] = this.getPref(name);
  2591.         break;
  2592.       }
  2593.     }
  2594.   }
  2595. ,
  2596.   
  2597.   get defaultDM() {
  2598.     return this.getPref("defaultDM", null);
  2599.   }
  2600. ,
  2601.   set defaultDM(name) {
  2602.     this.setPref("defaultDM", name);
  2603.     return name;
  2604.   }
  2605. ,
  2606.   get tmpDir() {
  2607.     return this.globals.tmpDir; 
  2608.   }
  2609. ,
  2610.   get profDir() {
  2611.     return this.globals.profDir; 
  2612.   }
  2613. ,
  2614.  
  2615.   get isWindows() {
  2616.     return ("nsIWindowsShellService" in CI) || ("@mozilla.org/winhooks;1" in CC);
  2617.   }
  2618. ,
  2619.   get DMS() {
  2620.     return this.globals.DMS;
  2621.   }
  2622. ,
  2623.   get extensions() {
  2624.     var s = this.getPref("extensions", "");
  2625.     return s ? s.split(/[\s,]+/) : [];
  2626.   },
  2627.   set extensions(v) {
  2628.     var arr = ((typeof(v) == "object" && v.join)
  2629.         ? v
  2630.         : typeof(v) == "string" && v && v.toLowerCase().split(',') || []
  2631.         ).map(function(e) { return e && e.replace(/[^\w\-]/g, '') })
  2632.          .filter(function(e) { return e });
  2633.     arr.sort();
  2634.     this.setPref("extensions", arr.join(','));
  2635.     return arr || [];
  2636.   },
  2637.   addExtension: function(e) {
  2638.     this.extensions = this.extensions.concat(e);
  2639.   }
  2640. ,
  2641.   extractIds: function(css) {
  2642.     var ids = css.match(/#[^ ,]+/g);
  2643.     for(var j = ids.length; j-- > 0; ids[j] = ids[j].substring(1));
  2644.     return ids;
  2645.   },
  2646.   hideNativeUI: function(document, selectors) {
  2647.     var s = selectors + " {display: none !important}";
  2648.     if("nsIDownloadManagerUI" in CI) { // Toolkit, sync stylesheets
  2649.       this.domUtils.updateStyleSheet(s, true);
  2650.     } else {
  2651.       for each (var id in this.extractIds(selectors)) try {
  2652.         document.getElementById(id).style.display = "none";
  2653.       } catch(e) {}
  2654.     }
  2655.     (document._FlashGot_NativeUI_styleSheets || 
  2656.       (document._FlashGot_NativeUI_styleSheets = [])
  2657.     ).push(s);
  2658.   },
  2659.   restoreNativeUIs: function(document) {
  2660.      var ss = document._FlashGot_NativeUI_styleSheets;
  2661.      if(!ss) return;
  2662.      var toolkit = "nsIDownloadManagerUI" in CI;
  2663.      var id;
  2664.      for each (var s in ss) {
  2665.        if(toolkit) {
  2666.          this.domUtils.updateStyleSheet(s, false);
  2667.        } else {
  2668.           for each (id in this.extractIds(s)) try {
  2669.             document.getElementById(id).style.display = "";
  2670.           } catch(e) {}
  2671.        }
  2672.      }
  2673.      document._FlashGot_NativeUI_styleSheets = null;
  2674.   },
  2675.   _httpServer: null,
  2676.   get httpServer() {
  2677.     if (typeof(FlashGotHttpServer) != "function") {
  2678.       CC["@mozilla.org/moz/jssubscript-loader;1"]
  2679.           .getService(CI.mozIJSSubScriptLoader)
  2680.           .loadSubScript("chrome://flashgot/content/flashgotHttpServer.js", null);
  2681.     }
  2682.     return ((!this._httpServer) || this._httpServer.isDown) ?
  2683.        this._httpServer=new FlashGotHttpServer(this)
  2684.       :this._httpServer;
  2685.   }
  2686.  
  2687. ,
  2688.   download: function(links, opType, dmName) {
  2689.     
  2690.     switch (links.length) {
  2691.       case 0: 
  2692.         return false;
  2693.       case 1: 
  2694.         opType = this.OP_ONE; 
  2695.         break;
  2696.       default:
  2697.         if (!opType) opType = this.OP_SEL;
  2698.     }
  2699.     
  2700.     if (!dmName) dmName = this.defaultDM;
  2701.     const dm = this.DMS[dmName];
  2702.     if (!dm) {
  2703.       this.log("FlashGot error: no download manager selected!");
  2704.       return false;
  2705.     }
  2706.     
  2707.     // surrogate missing attributes
  2708.     
  2709.     if (!links.progress) {
  2710.       links.progress = { update: function() {} };
  2711.     } else {
  2712.       links.progress.update(12);
  2713.     }
  2714.     
  2715.    
  2716.     var service = this;
  2717.     this._delay(function(t) { service._downloadDelayed(links, opType, dm); }); 
  2718.     return true;
  2719.   },
  2720.   
  2721.   _downloadDelayed: function(links, opType, dm) {
  2722.     
  2723.      if (!links.postData) { 
  2724.       links.postData = null;
  2725.     } else if(!dm.postSupport) {
  2726.       // surrogate POST parameters as query string
  2727.       links[0].href += (links[0].href.indexOf("?") > -1 ?  "&" : "?") + links.postData;
  2728.     }
  2729.  
  2730.     const encodedURLs = this.getPref(dm.getPref("encode"), this.getPref("encode", true));
  2731.  
  2732.     const extFilter = this.getPref("extfilter", false) && !this.interceptor.interceptAll ?
  2733.         new RegExp("\.(" +
  2734.           this.extensions.join("|").replace(/[^\w-|]/,"") + 
  2735.           ")\\b", "i") : null;
  2736.     
  2737.     var logMsg = "Processing "+links.length+" links ";
  2738.     if (this.logEnabled && typeof(links.startTime) == "number") {
  2739.       logMsg += "scanned in ms" + (Date.now() - links.startTime);
  2740.     }
  2741.     
  2742.     
  2743.  
  2744.     if (!links.startTime) links.startTime = Date.now();
  2745.     const pg = links.progress;
  2746.     
  2747.     const escapeCheckNo=/(%[0-9a-f]{2,4})/i;
  2748.     const escapeCheckYes=/[\s]+/;
  2749.     
  2750.     var len = links.length;
  2751.     
  2752.     var filters = null;
  2753.     
  2754.     if (len > 1) {
  2755.       filters = [];
  2756.       
  2757.       const isValid = dm.isValidLink; 
  2758.       if (isValid)  filters.push(function(href) { return isValid(href) });
  2759.       if (extFilter) filters.push(function(href) { return extFilter.test(href) });
  2760.  
  2761.       if (filters.length) {
  2762.         filters.doFilter = function(href) {
  2763.           for (var j = this.length; j-- > 0;) if(!this[j](href)) return false;
  2764.           return true;
  2765.         }
  2766.       } else {
  2767.         filters = null;
  2768.       }
  2769.     }
  2770.     
  2771.     
  2772.     const map = {};
  2773.     pg.update(10);
  2774.     
  2775.     const stripHash = dm.getPref("stripHash", false);
  2776.     
  2777.     var j, l, href, ol, pos1, pos2;
  2778.     for (j = 0; j < len; j++) {
  2779.       l = links[j];
  2780.       l._pos = j;
  2781.       href = l.href;
  2782.       if ((!filters) || filters.doFilter(href)) {
  2783.         ol = map[href];
  2784.         if (ol) { // duplicate, keep the longest description
  2785.           if (ol.description.length < l.description.length) {
  2786.             map[href] = l;
  2787.             l.href = ol.href; // keep sanitizations
  2788.           }
  2789.         } else {
  2790.           map[href] = l;
  2791.           
  2792.           // encoding checks
  2793.           try {
  2794.             if (encodedURLs) { 
  2795.               if (escapeCheckYes.test(href) || !escapeCheckNo.test(href)) { 
  2796.                 href = encodeURI(href);
  2797.               }
  2798.               // workaround for malformed hash urls
  2799.              
  2800.               while ((pos1 = href.indexOf("#")) > -1 // has fragment?
  2801.                 && href[pos1 + 1] != "!" // skip metalinks!
  2802.                 && (href.indexOf("?") > pos1 || pos1 != href.lastIndexOf('#')) // fragment before query or double fragment ? 
  2803.               ) {
  2804.                 href = href.substring(0, pos1) + '%23' + href.substring(pos1 + 1);
  2805.               }
  2806.               
  2807.               l.href = href;
  2808.             } else {  
  2809.               l.href = decodeURI(href);
  2810.             }
  2811.             if (stripHash) l.href = l.href.replace(/#.*/g, '');
  2812.             
  2813.           } catch(e) {
  2814.             dump("Problem "
  2815.               + ( encodedURLs ? "escaping" : "unescaping")
  2816.               + " URL " + href + ": "+ e.message + "\n");
  2817.           }
  2818.         }
  2819.       }
  2820.     }
  2821.     pg.update(25);
  2822.     
  2823.     links.length = 0;
  2824.     for (href in map) links[links.length] = map[href];
  2825.     
  2826.     if (this.getPref("noDesc", false) || dm.getPref("noDesc", false)) {
  2827.       for (j = links.length; j-- > 0;) links[j].description = '';
  2828.     } else if(dm.asciiFilter) {
  2829.       for (j = links.length; j-- > 0;) {
  2830.         l = links[j];
  2831.         if(l.description) 
  2832.           l.description = l.description.replace(/[^\u0020-\u007f]/g, "") || l.href;
  2833.       }
  2834.     }
  2835.     
  2836.     this._processRedirects(links, opType, dm);
  2837.   },
  2838.   
  2839.   _processRedirects: function(links, opType, dm) {
  2840.     links.progress.update(30);
  2841.     var service = this;
  2842.     this._delay(function() {
  2843.       new RedirectContext(links, opType, dm, function(processedBy) {
  2844.         links.redirProcessedBy = processedBy;
  2845.         service._sendToDM(links, opType, dm);
  2846.         service = null;
  2847.       }).process();
  2848.     });
  2849.   },
  2850.   
  2851.   get _redirectContext() { return RedirectContext }, // ease debug
  2852.   
  2853.   _sendToDM: function(links, opType, dm) {
  2854.     
  2855.     if (this.getPref("httpauth", false)) {
  2856.       dm.log("Adding authentication info");
  2857.       this._addAuthInfo(links);
  2858.     }
  2859.     
  2860.     if (dm.metalinkSupport && this.getPref("metalink", true)) {
  2861.       dm.log("Adding metalink info");
  2862.       if (this._processMetalinks(links)) {
  2863.         opType = this.OP_SEL; // force "ask path"
  2864.       }
  2865.     }
  2866.     
  2867.     if (links.length > 1) {
  2868.       dm.log("Sorting again "+links.length+" links");
  2869.       links.sort(function(a,b) {
  2870.         a=a._pos; b=b._pos;
  2871.         return a>b?1:a<b?-1:0;
  2872.       });
  2873.     }
  2874.     
  2875.     this._addQsSuffix(links);
  2876.     
  2877.     links.progress.update(70);
  2878.     
  2879.     dm.log("Preprocessing done in ms" + (Date.now() - links.startTime) );
  2880.     
  2881.     // "true" download
  2882.     this._delay(function(t) {
  2883.         dm.log("Starting dispatch");
  2884.         var startTime = Date.now();
  2885.     
  2886.         dm.download(links, opType);
  2887.  
  2888.         var now = Date.now();
  2889.         var logMsg = "Dispatch done in ms" + (now - startTime);
  2890.         if (typeof(links.startTime) == "number") { 
  2891.           logMsg += "\nTotal processing time: ms" + (now - links.startTime);
  2892.         }  
  2893.         dm.log(logMsg);
  2894.       });
  2895.   },
  2896.   
  2897.   _addQsSuffix: function(links) {
  2898.     var suffix = this.getPref("queryStringSuffix");
  2899.     if (suffix) {
  2900.       var rep = function(url, most, qs, hash) {
  2901.         return most + (qs ? qs + "&" : "?") + suffix + hash;
  2902.       }
  2903.       var l;
  2904.       for (var j = links.length; j-- > 0;) {
  2905.         l = links[j];
  2906.         l.href = l.href.replace(/^(.*?)(\?[^#]*)?(#.*)?$/, rep);
  2907.       }
  2908.     }
  2909.   },
  2910.   
  2911.   _addAuthInfo: function(links) {
  2912.     const httpAuthManager = CC['@mozilla.org/network/http-auth-manager;1']
  2913.                               .getService(CI.nsIHttpAuthManager);
  2914.     const ioService = CC["@mozilla.org/network/io-service;1"]
  2915.                         .getService(CI.nsIIOService);
  2916.     var uri;
  2917.     var udom = {};
  2918.     var uname = {};
  2919.     var upwd = {};
  2920.     var l;
  2921.     for (var j = links.length; j-- > 0;) {
  2922.       l = links[j];
  2923.       try {
  2924.         uri = ioService.newURI(l.href, null, null);
  2925.         if (l.userPass && l.userPass.indexOf(":") > -1) continue;
  2926.         httpAuthManager.getAuthIdentity(uri.scheme, uri.host, uri.port < 0 ? (uri.scheme == "https" ? 443 : 80) : uri.port, null, null, uri.path, udom, uname, upwd);
  2927.         this.log("Authentication data for " + uri + " added.");
  2928.         l.href = uri.scheme + "://" + uname.value + ":" + upwd.value + "@" + 
  2929.                  uri.host + (uri.port < 0 ? "" : (":" + uri.port)) + uri.spec.substring(uri.prePath.length);
  2930.       } catch(e) {}
  2931.     }
  2932.   },
  2933.   _processMetalinks: function(links) {
  2934.     var hasMetalinks = false;
  2935.     var l, k, href, pos, parts, couple, key;
  2936.     for (var j = links.length; j-- > 0;) {
  2937.        l = links[j];
  2938.        href = l.href;
  2939.        pos = href.indexOf("#!");
  2940.        if (pos < 0) continue;
  2941.        parts = href.substring(pos + 2).split("#!");
  2942.        if (parts[0].indexOf("metalink3!") == 0) continue; // per Ant request
  2943.        
  2944.        hasMetalinks = true;
  2945.        l.metalinks = [];
  2946.        for (k = 0; k < parts.length; k++) {
  2947.          couple = parts[k].split("!");
  2948.          if (couple.length != 2) continue;
  2949.          key = couple[0].toLowerCase();
  2950.          switch (key) {
  2951.            case "md5": case "sha1":
  2952.              l[key] = couple[1];
  2953.              break;
  2954.            case "metalink":
  2955.             if (/^(https?|ftp):/i.test(couple[1])) {
  2956.               l.metalinks.push(couple[1]);
  2957.             }
  2958.             break;
  2959.          }
  2960.        }
  2961.     }
  2962.     return links.hasMetalinks = hasMetalinks;
  2963.   }
  2964. ,
  2965.   _delay: function(callback, time) {
  2966.      var timerCallback = { notify: callback }; 
  2967.      CC["@mozilla.org/timer;1"].createInstance(CI.nsITimer)
  2968.               .initWithCallback(timerCallback, time || 0, CI.nsITimer.TYPE_ONE_SHOT);
  2969.   }
  2970. ,
  2971.   yield: function() {
  2972.     try {
  2973.       const eqs = CI.nsIEventQueueService;
  2974.       if (eqs) {
  2975.         CC["@mozilla.org/event-queue-service;1"]
  2976.           .getService(eqs).getSpecialEventQueue(eqs.UI_THREAD_EVENT_QUEUE)
  2977.           .processPendingEvents();
  2978.       } else {
  2979.         const curThread = CC["@mozilla.org/thread-manager;1"].getService().currentThread;
  2980.         while (curThread.hasPendingEvents()) curThread.processNextEvent(false);
  2981.       }
  2982.     } catch(e) {}
  2983.   },
  2984.   
  2985.   
  2986.   
  2987.   get bgProcessing() {
  2988.     return false;
  2989.       // this.getPref("bgProcessing", true);
  2990.   }
  2991. ,
  2992.   get prefService() {
  2993.     return CC["@mozilla.org/preferences-service;1"].getService(
  2994.       CI.nsIPrefService);
  2995.   }
  2996. ,
  2997.   savePrefs: function() {
  2998.     return this.prefService.savePrefFile(null);
  2999.   }
  3000. ,
  3001.   getPref: function(name, def) {
  3002.     const IPC = CI.nsIPrefBranch;
  3003.     const prefs = this.prefs;
  3004.     try {
  3005.       switch (prefs.getPrefType(name)) {
  3006.         case IPC.PREF_STRING:
  3007.           return prefs.getCharPref(name);
  3008.         case IPC.PREF_INT:
  3009.           return prefs.getIntPref(name);
  3010.         case IPC.PREF_BOOL:
  3011.           return prefs.getBoolPref(name);
  3012.       }
  3013.     } catch(e) {}
  3014.     return def;
  3015.   }
  3016. ,
  3017.   setPref: function(name,value) {
  3018.     const prefs=this.prefs;
  3019.     switch (typeof(value)) {
  3020.       case "string":
  3021.           prefs.setCharPref(name,value);
  3022.           break;
  3023.       case "boolean":
  3024.         prefs.setBoolPref(name,value);
  3025.         break;
  3026.       case "number":
  3027.         prefs.setIntPref(name,value);
  3028.         break;
  3029.       default:
  3030.         throw new Error("Unsupported type "+typeof(value)+" for preference "+name);
  3031.     }
  3032.   }
  3033. ,
  3034.   strings: new Strings("flashgot"),
  3035.   getString: function(name, parms) {
  3036.     return this.strings.getString(name, parms);
  3037.   }
  3038. ,
  3039.   _logFile: null,
  3040.   get logFile() {
  3041.     if (this._logFile==null) {
  3042.       this._logFile=this.profDir.clone();
  3043.       this._logFile.append("flashgot.log");
  3044.     }
  3045.     return this._logFile;
  3046.   }
  3047. ,
  3048.   logStream: null,
  3049.   logEnabled: false,
  3050.   log: function(msg) {
  3051.     if (this.logEnabled) {
  3052.       try {
  3053.         if (!this.logStream) {
  3054.           const logFile=this.logFile;
  3055.           const logStream=CC["@mozilla.org/network/file-output-stream;1"
  3056.             ].createInstance(CI.nsIFileOutputStream );
  3057.           logStream.init(logFile, 0x02 | 0x08 | 0x10, 0600, 0 );
  3058.           this.logStream=logStream;
  3059.           const header="*** Log start at "+new Date().toGMTString()+"\n";
  3060.           this.logStream.write(header,header.length);
  3061.         }
  3062.         
  3063.         if (msg!=null) {
  3064.           msg+="\n";
  3065.           this.logStream.write(msg,msg.length);
  3066.         }
  3067.         this.logStream.flush();
  3068.       } catch(ex) {
  3069.         dump(ex.message+"\noccurred logging this message:\n"+msg);
  3070.       }
  3071.     }
  3072.   }
  3073. ,
  3074.   dumpStack: function(msg) {
  3075.     dump( (msg?msg:"")+"\n"+new Error().stack+"\n");
  3076.   }
  3077. ,
  3078.   clearLog: function() {
  3079.     try {
  3080.       if (this.logStream) {
  3081.         try {
  3082.           this.logStream.close();
  3083.         } catch(eexx) {
  3084.           dump(eexx.message);
  3085.         }
  3086.       }
  3087.       if (this.logFile) this.logFile.remove(true);
  3088.       this.logStream=null;
  3089.       this.log(null);
  3090.     } catch(ex) { dump(ex.message); }
  3091.   } 
  3092. ,
  3093.   get windowMediator() {
  3094.     return CC["@mozilla.org/appshell/window-mediator;1"
  3095.       ].getService(CI.nsIWindowMediator);
  3096.   }
  3097. ,
  3098.   getWindow: function() {
  3099.     return this.windowMediator.getMostRecentWindow(null);
  3100.   }
  3101. ,
  3102.   getBrowserWindow: function(document) {
  3103.     if (!document) return null;
  3104.     var w = DOMUtils.getChromeWindow(document.defaultView.top);
  3105.     return w.wrappedJSObject || w; 
  3106.   }
  3107. ,
  3108.   _globals: null,
  3109.   get globals() {
  3110.     if (!this._initialized) {
  3111.       this.initGlobals();
  3112.     }
  3113.     return this._globals;
  3114.   }
  3115. ,
  3116.   PREFS_BRANCH: "flashgot."
  3117. ,
  3118.   _prefs: null,
  3119.   get prefs() {
  3120.     return this._prefs || (this._prefs = this.prefService.getBranch(this.PREFS_BRANCH).QueryInterface(CI.nsIPrefBranchInternal));
  3121.   }
  3122. ,
  3123.   _initialized: false,
  3124.   initGlobals: function() {
  3125.     if (this._globals || this._initialized) return;
  3126.     
  3127.     function prepareTmp(t) {
  3128.       t.append("flashgot."+encodeURI(profDir.leafName).replace(/%/g,"_"));
  3129.       if (t.exists()) {
  3130.        if (!t.isDirectory()) t.createUnique(1, 0700);
  3131.       } else {
  3132.         t.create(1,0700);
  3133.       }
  3134.       return t;
  3135.     }
  3136.     
  3137.     try {
  3138.       const startTime = Date.now();
  3139.       const prefs = this.prefs;
  3140.  
  3141.       
  3142.       const fileLocator = CC["@mozilla.org/file/directory_service;1"].getService(
  3143.         CI.nsIProperties);
  3144.       const profDir = fileLocator.get("ProfD",CI.nsIFile);
  3145.      
  3146.       var tmpDir;
  3147.       try {
  3148.         tmpDir = prepareTmp(prefs.getComplexValue("tmpDir", CI.nsILocalFile));
  3149.       } catch(ex) {
  3150.         tmpDir = prepareTmp(fileLocator.get("TmpD", CI.nsILocalFile));
  3151.       }
  3152.        
  3153.       this._globals={
  3154.         tmpDir: tmpDir,
  3155.         profDir: profDir,
  3156.         prefs: prefs
  3157.       };
  3158.       
  3159.       prefs.addObserver("", this, false);
  3160.       this.syncPrefs();
  3161.       
  3162.       this.log("Per-session init started");
  3163.         
  3164.       this._setupLegacyPrefs();
  3165.  
  3166.       this._globals.DMS = this.checkDownloadManagers(true, false);
  3167.       this.log("Per-session init done in " + (Date.now() - startTime) + "ms");
  3168.     } catch(initEx) {
  3169.       this._initException = initEx;
  3170.       try { this.log(initEx); } catch(e) {}
  3171.     }
  3172.     this._initialized=true; 
  3173.   }
  3174. ,
  3175.   dispose: function() {
  3176.     this.prefs.removeObserver("",this);
  3177.     this._prefs=null;
  3178.     this._initialized=false;
  3179.     this._globals=null;
  3180.   }
  3181. ,
  3182.   createCustomDM: function(name) {
  3183.     const dm = new FlashGotDMCust(name);
  3184.     if (name && name.length) {
  3185.       FlashGotDMCust.persist(this);
  3186.       this.sortDMS();
  3187.       this.checkDownloadManagers(false, false);
  3188.     }
  3189.     return dm;
  3190.   }
  3191. ,
  3192.  removeCustomDM: function(name) {
  3193.    const dms = FlashGotDM.dms;
  3194.    for (var j = dms.length; j-->0;) {
  3195.      if (dms[j].custom && dms[j].name == name) {
  3196.        dms.splice(j, 1);
  3197.        delete dms[name];
  3198.      }
  3199.    }
  3200.    FlashGotDMCust.persist(this);
  3201.    this.checkDownloadManagers(false, false);
  3202.  }
  3203. ,
  3204.   sortDMS: function() {
  3205.     FlashGotDM.dms.sort(function(a,b) { 
  3206.       a = a.priority || a.name.toLowerCase(); 
  3207.       b = b.priority || b.name.toLowerCase();
  3208.       return a > b ? 1 : a < b ?-1 : 0; 
  3209.     });
  3210.   }
  3211.   checkDownloadManagers: function(init, detect) {
  3212.     
  3213.     if (init || detect) FlashGotDM.init(this);
  3214.     
  3215.     const dms = FlashGotDM.dms;
  3216.     dms.found = false;
  3217.     var defaultDM = this.defaultDM;
  3218.     if (!dms[defaultDM]) defaultDM = null;
  3219.     
  3220.     detect = detect || this.getPref("detect.auto", true);
  3221.  
  3222.     var j, dm;
  3223.     var cache;
  3224.     
  3225.     if (!detect) {
  3226.       cache = this.getPref("detect.cache", "").split(",");
  3227.       for (j = dms.length; j-- > 0;) {
  3228.         dm = dms[j];
  3229.         if (!dm.custom) dm._supported = false;
  3230.       }
  3231.       var name;
  3232.       for (j = cache.length; j-- > 0;) {
  3233.         name = cache[j];
  3234.         if (name.length && typeof(dm = dms[name])=="object" && dm.name == name) {
  3235.           dm._supported = true;
  3236.         }
  3237.       }
  3238.     }
  3239.     
  3240.     cache = [];
  3241.     var exclusive;
  3242.     var firstSupported=null;
  3243.     for (j = dms.length; j-- >0;) {
  3244.       dm=dms[j];
  3245.       if (dm.supported) {
  3246.         dms.found = true;
  3247.         cache[cache.length] = firstSupported = dm.name;
  3248.         if (dm.exclusive) exclusive=true;
  3249.       } else {
  3250.         this.log("Warning: download manager " + dm.name + " not found");
  3251.         if (defaultDM == dm.name) {
  3252.           defaultDM = null;
  3253.           this.log(dm.name + " was default download manager: resetting.");
  3254.         }
  3255.       }
  3256.     }
  3257.     
  3258.     this.setPref("detect.cache", cache.join(","));
  3259.     
  3260.     if ((!defaultDM) && firstSupported != null) {
  3261.       this.defaultDM = firstSupported;
  3262.       this.log("Default download manager set to " + this.defaultDM);
  3263.     } else if(!dms.found) {
  3264.       this.log("Serious warning! no supported download manager found...");
  3265.     } 
  3266.     if (exclusive) {
  3267.       for (j=dms.length; j-->0;) {
  3268.         if (! (dms[j].custom || dms[j].supported) ) {
  3269.           dms.splice(j,1);
  3270.         }
  3271.       }
  3272.     }
  3273.     
  3274.     return dms;
  3275.   }
  3276. ,
  3277.   _referrerSpoofer: null,
  3278.   get referrerSpoofer() {
  3279.     if (typeof(ReferrerSpoofer) != "function") {
  3280.       CC["@mozilla.org/moz/jssubscript-loader;1"]
  3281.           .getService(CI.mozIJSSubScriptLoader)
  3282.           .loadSubScript("chrome://flashgot/content/referrerSpoofer.js", null);
  3283.     }
  3284.     return (!this._httpServer) ? this._referrerSpoofer = new ReferrerSpoofer() :this._referrerSpoofer;
  3285.   }
  3286. ,
  3287.   _cleaningup: false
  3288. ,
  3289.   cleanup: function() {
  3290.     if (this._cleaningup) return;
  3291.     try {
  3292.       this._cleaningup = true;
  3293.       this.log("Starting cleanup");
  3294.       if (this._httpServer) {
  3295.         this._httpServer.shutdown();
  3296.       }
  3297.       
  3298.       try {
  3299.         FlashGotDM.cleanup(this.uninstalling);
  3300.       } catch(eexx) {
  3301.         dump(eexx.message);
  3302.       }
  3303.       
  3304.       if (this._globals && this._globals.tmpDir.exists()) {
  3305.         try {
  3306.           this._globals.tmpDir.remove(true);
  3307.         } catch(eexx) {
  3308.           this.log("Can't remove " + this._globals.tmpDir.path + ", maybe still in use: " + eexx);
  3309.         }
  3310.       }
  3311.       this._bundle = null;
  3312.       this.log("Cleanup done");
  3313.       if (this._logFile) try {
  3314.         if (this.logStream) this.logStream.close();
  3315.         var maxLogSize = Math.max(Math.min(this.getPref('maxLogSize',100000),1000000),50000);
  3316.         const logFile = this.logFile;
  3317.         const logSize = logFile.fileSize;
  3318.         const logBak = logFile.clone();
  3319.         logBak.leafName = logBak.leafName+".bak";
  3320.         if (logBak.exists()) logBak.remove(true);
  3321.           
  3322.         if (this.uninstalling) {
  3323.           logFile.remove(false);
  3324.         } else if (logSize > maxLogSize) { // log rotation
  3325.           // dump("Cutting log (size: "+logSize+", max: "+maxLogSize+")");
  3326.  
  3327.           logFile.copyTo(logBak.parent, logBak.leafName);
  3328.           const is=CC['@mozilla.org/network/file-input-stream;1'].createInstance(
  3329.             CI.nsIFileInputStream);
  3330.           is.init(logBak,0x01, 0400, null);
  3331.           is.QueryInterface(CI.nsISeekableStream);
  3332.           is.seek(CI.nsISeekableStream.NS_SEEK_END,-maxLogSize);
  3333.           const sis=CC['@mozilla.org/scriptableinputstream;1'].createInstance(
  3334.           CI.nsIScriptableInputStream);
  3335.           sis.init(is);
  3336.           var buffer;
  3337.           var content="\n";
  3338.           var logStart=-1;
  3339.           while ((buffer=sis.read(5000))) {
  3340.             content+=buffer;
  3341.             if ((logStart=content.indexOf("\n*** Log start at "))>-1) { 
  3342.               content=content.substring(logStart);
  3343.               break;
  3344.             }
  3345.             content=buffer;
  3346.           }
  3347.           if (logStart>-1) {
  3348.              const os=CC["@mozilla.org/network/file-output-stream;1"].createInstance(
  3349.               CI.nsIFileOutputStream);
  3350.             os.init(logFile,0x02 | 0x08 | 0x20, 0700, 0);
  3351.             os.write(content,content.length);
  3352.             while ((buffer=sis.read(20000))) {
  3353.               os.write(buffer,buffer.length);
  3354.             } 
  3355.             os.close();
  3356.           }
  3357.           sis.close();
  3358.         }
  3359.       } catch(eexx) {
  3360.         dump("Error cleaning up log: "+eexx);
  3361.       }
  3362.       this.logStream = null;
  3363.     } catch(ex) {
  3364.        this.log(ex);
  3365.     }
  3366.     this._cleaningup = false;
  3367.     this.dispose();
  3368.   }
  3369. ,
  3370.   readFile: function(file) {
  3371.     const is = CC["@mozilla.org/network/file-input-stream;1"].createInstance(
  3372.           CI.nsIFileInputStream );
  3373.     is.init(file ,0x01, 0400, null);
  3374.     const sis = CC["@mozilla.org/scriptableinputstream;1"].createInstance(
  3375.       CI.nsIScriptableInputStream );
  3376.     sis.init(is);
  3377.     const res = sis.read(sis.available());
  3378.     is.close();
  3379.     return res;
  3380.   }
  3381. ,
  3382.   writeFile: function(file, content, charset) {
  3383.     const unicodeConverter = CC["@mozilla.org/intl/scriptableunicodeconverter"]
  3384.       .createInstance(CI.nsIScriptableUnicodeConverter);
  3385.     try {
  3386.       unicodeConverter.charset = charset ? charset : "UTF-8";
  3387.     } catch(ex) {
  3388.       unicodeConverter.charset = "UTF-8";
  3389.     }
  3390.     
  3391.     function hex(s) {
  3392.       
  3393.     }
  3394.     
  3395.     
  3396.     this.log("Converting " + content.length + " long job to charset " + unicodeConverter.charset);
  3397.     //this.logHex(content);
  3398.     content = unicodeConverter.ConvertFromUnicode(content);
  3399.     this.log("Writing " + content.length + " bytes...");
  3400.     // this.logHex(content);
  3401.     const os = CC["@mozilla.org/network/file-output-stream;1"]
  3402.       .createInstance(CI.nsIFileOutputStream);
  3403.     os.init(file, 0x02, 0700, 0);
  3404.     os.write(content, content.length);
  3405.     os.close();
  3406.   }
  3407. ,
  3408.   
  3409.   logHex: function(s) {
  3410.     var cc = [];
  3411.     for(var j = 0, len = s.length; j < len; j++) {
  3412.       cc.push(s.charCodeAt(j).toString(16));
  3413.     }
  3414.     this.log(cc.join(","));
  3415.   }
  3416. ,
  3417.   _lookupMethod: null,
  3418.   get lookupMethod() {
  3419.     return this._lookupMethod?this._lookupMethod:(this._lookupMethod = 
  3420.       (Components.utils && Components.utils.lookupMethod)
  3421.         ?Components.utils.lookupMethod:Components.lookupMethod);
  3422.   }
  3423. ,
  3424.   _setupLegacyPrefs: function() {
  3425.     // check and move flashgot.flashgot.dmsopts branch from previous bug
  3426.     try {
  3427.       for each (var key in this.prefs.getChildList("flashgot.dmsopts.", {})) {
  3428.         this.setPref(key.replace(/^flashgot\./, ""), this.getPref(key));
  3429.       }
  3430.       this.prefs.deleteBranch("flashgot.dmsopts.");
  3431.     } catch(e) {
  3432.       dump(e + "\n");
  3433.     }
  3434.   }
  3435. ,
  3436.   showDMSReference: function() {
  3437.     this.getWindow().open("http://www.flashgot.net/dms","_blank");
  3438.   }
  3439.   dirtyJobsDone: false
  3440. }
  3441.  
  3442. var RedirectContext = function(links, opType, dm, onfinish) {
  3443.   this.links = links;
  3444.   this.opType = opType;
  3445.   this.dm = dm;
  3446.   this.onfinish = onfinish || function() {};
  3447.   this.processedBy = {};
  3448.   this.redirects = 0;
  3449.   this.maxRedirects = 1;
  3450.   var processors = [];
  3451.   var srv = dm.service;
  3452.   for each(var p in this.processors) {
  3453.     if (srv.getPref("redir." + p.name + ".enabled", true))
  3454.       processors.push(p);
  3455.   }
  3456.   
  3457.   this.processors = processors;
  3458. };
  3459.  
  3460. RedirectContext.prototype = {
  3461.   prefs: CC["@mozilla.org/preferences-service;1"].getService(CI.nsIPrefService).getBranch("flashgot.redir."),
  3462.   
  3463.   print: Components.utils && Components.utils.reportError || dump,
  3464.   log: function(msg) {
  3465.     this.print("[FlashGot Redirect Processor] " + msg);
  3466.   },
  3467.   process: function(links) {
  3468.     if(!links) links = this.links;
  3469.     try {
  3470.       this.start();
  3471.       for (j = links.length; j-- > 0;) {
  3472.         this.processLink(links[j]);
  3473.       }
  3474.     } catch(e) {
  3475.       this.log(e);
  3476.     } finally {
  3477.       this.done();
  3478.     }
  3479.   },
  3480.   
  3481.   processLink: function(l) {
  3482.     const processors = this.processors;
  3483.     for (var p = 0, plen = processors.length, j; p < plen; p++) {
  3484.       try {
  3485.         processors[p](l, this);
  3486.       } catch(e) {
  3487.         this.log(processors[p].name + ": " + e + " " + e.stack);
  3488.       }
  3489.     }
  3490.   },
  3491.   
  3492.   start: function() {
  3493.     this.redirects++;
  3494.   },
  3495.   done: function() {
  3496.     if(this.redirects > this.maxRedirects) this.maxRedirects = this.redirects;
  3497.     if (--this.redirects <= 0) {
  3498.       this.onfinish(this.processedBy);
  3499.     }
  3500.     if(this.redirects >= 0) {
  3501.       this.links.progress.update(
  3502.           40 + 30 * (this.maxRedirects - this.redirects) / this.maxRedirects);
  3503.     }
  3504.   },
  3505.   change: function(l, newURL, processedBy, multiReplace) {
  3506.     this.processedBy[processedBy || arguments.callee.caller.name] = true;
  3507.     if(!this.links.some(function(l) { return l.href == newURL })) {
  3508.       
  3509.       var nl;
  3510.       if (multiReplace) {
  3511.         nl = {};
  3512.         for(var p in l) nl[p] = l[p];
  3513.         this.links.push(nl);
  3514.         var pos = this.links.indexOf(l);
  3515.         if (pos > -1) this.links.splice(pos, 1);
  3516.       } else {
  3517.         nl = l;
  3518.       }
  3519.       nl.href = newURL;
  3520.       nl.contentType = null; // prevent spidering
  3521.       this.processLink(nl); // recursive processing
  3522.     }
  3523.   },
  3524.   createReq: function() {
  3525.     return CC["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(CI.nsIXMLHttpRequest);
  3526.   },
  3527.   
  3528.   load: function(url, callbacks, data) {
  3529.     
  3530.     if (typeof(data) == "undefined") data = null;
  3531.     
  3532.     var req = this.createReq();
  3533.     req.open(data == null ?  "GET" :"POST", url, true);
  3534.     if (data != null) req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
  3535.     
  3536.     if (typeof(callbacks) != "object") {
  3537.       callbacks = { ok: callbacks };
  3538.     }
  3539.     var context = this;
  3540.     callbacks.call = function(phase) {
  3541.       if (typeof(this[phase]) == "function") this[phase](req, context);
  3542.     }
  3543.     
  3544.     callbacks.call(0);
  3545.     
  3546.     req.onreadystatechange = function() {
  3547.       var phase = req.readyState;
  3548.       try {
  3549.         callbacks.call(phase);
  3550.       } catch(e) {
  3551.         context.log(e);
  3552.       }
  3553.       if (phase == 4) {
  3554.         try {
  3555.           if (req.status == 200) callbacks.call("ok");
  3556.         } catch(e) {
  3557.           context.log(e);
  3558.         } finally {
  3559.           context.done();
  3560.           context = null;
  3561.         }
  3562.       }
  3563.     };
  3564.     
  3565.     this.start();
  3566.     try {
  3567.       req.send(data); 
  3568.     } catch(e) {
  3569.       this.done();
  3570.     }
  3571.   },
  3572.   
  3573.   processors: [
  3574.     function anonym_to(l, context) { // anonym.to, anonymz.com, linkbucks.com
  3575.       var m = l.href.match(/^http:\/\/(?:[^\.\/]+\.)?(linkbucks\.com|anonym\.to|anonymz\.com)(?:\/?.*?)?\?.*?(http.*)/i);
  3576.       if (m) {
  3577.         var href = m[2];
  3578.         context.change(l, /^http%3a/i.test(href) ? unescape(href) : href, m[1].replace(".", "_"));
  3579.       }
  3580.     },
  3581.     
  3582.     function ftp2share_net(l, context) {
  3583.         if (/^https?:\/\/ftp2share\.net\//.test(l.href)) {
  3584.           var processedBy = arguments.callee.name;
  3585.           context.load(l.href, function(req) {
  3586.             var mm = req.responseText.match(/javascript:\s*go\s*\(((["']).*\2)/g);
  3587.             if (mm) {
  3588.               for(var j = 0, len = mm.length; j < len; j++) {
  3589.                 try {
  3590.                   context.change(l, atob(mm[j].replace(/[^A-Za-z0-9\+\/\=]|javascript:.*?go/g, "")), processedBy, true);
  3591.                 } catch(e) {
  3592.                   context.log(e);
  3593.                 }
  3594.               }
  3595.             }
  3596.           }, "download=true");
  3597.           
  3598.         }
  3599.     },
  3600.     
  3601.     function depositfiles_com(l, context) {
  3602.       if (/^http:\/\/depositfiles\.com\//.test(l.href))
  3603.         context.load(l.href, function(req) {
  3604.           var m = req.responseText.match(/https?:\/\/[^"'\s]+depositfiles\.com\/auth\-[^"' ]+/);
  3605.           if (m) context.change(l, m[0]);
  3606.         });
  3607.     },
  3608.     
  3609.     function hideurl_biz(l, context) {
  3610.       if (/^http:\/\/hideurl\.biz\//.test(l.href)) {
  3611.         if (/\/link\.php/.test(l.href)) {
  3612.           var processedBy = arguments.callee.name;
  3613.           if (context.sniffRedir(l.href, function(url) {
  3614.             if(url) context.change(l, url, processedBy);
  3615.             context.done();
  3616.             }))
  3617.             context.start();
  3618.         } else {
  3619.           context.load(l.href, function(req) {
  3620.             var m = req.responseText.match(/http:\/\/hideurl\.biz\/link\.php[^'"]+/g);
  3621.             if (m) {
  3622.               for each(var u in m) {
  3623.                 context.change(l, u, processedBy, true);
  3624.               }
  3625.             }
  3626.           });
  3627.         }
  3628.       }
  3629.     },
  3630.     
  3631.     function lix_in(l, context) {
  3632.       if (/^http:\/\/lix\.in\//.test(l.href)) 
  3633.         context.load(l.href, function(req) {
  3634.            var m = req.responseText.match(/<iframe[^>]*src\s*=\s*['"]([^"']+).*/);
  3635.            if (m) context.change(l, m[1]);
  3636.         }, "tiny=" + l.href.replace(/.*lix\.in\//, "") + "&submit=continue"); 
  3637.     },
  3638.  
  3639.     function link_protector_com(l, context) {
  3640.       if (!/^http:\/\/link-protector\.com\//.test(l.href)) return;
  3641.       function addRef(req) { req.setRequestHeader("Referer", context.links.referrer); }
  3642.       context.load(l.href,
  3643.         {
  3644.           0: addRef,
  3645.           1: addRef,
  3646.           ok: function(req) {
  3647.             var m = req.responseText.match(/yy\[i\]\s*-(\d+)[\S\s]+stream\(['"]([^'"]+)/);
  3648.             if (m) {
  3649.               function decode(t, x) {
  3650.                 function stream(prom){var yy=new Array();for(i=0; i*4 <prom.length; i++){yy[i]=prom.substr(i*4,4);}yy.reverse();var xstream=new String;for (var i = 0; i < yy.length; i++){xstream+=String.fromCharCode(yy[i]-x);}return xstream;}
  3651.                 return stream(t);
  3652.               }
  3653.               context.change(l, decode(m[2], m[1]).match(/="(https?:[^" ]+)/)[1]); 
  3654.             } else if((m = req.responseText.match(/<a href="(https?:[^" ]+)/))) {
  3655.               context.change(l, m[1]);
  3656.             }
  3657.           }
  3658.         });
  3659.     },
  3660.     
  3661.     function linkbank_eu(l, context) {
  3662.       if (!/^http:\/\/(?:[\w-]+\.)linkbank.eu\/show\.php/.test(l.href)) return;
  3663.       
  3664.       var posli = l.href.replace(/show.*/, "posli.php?match=");
  3665.       context.load(l.href, function(req) {
  3666.           var m = req.responseText.match(/posli\("\d+",\s*"\d+"\)/g);
  3667.           if (!m) return;
  3668.           for each(var sm in m) {
  3669.             sm = sm.match(/posli\("(\d+)",\s*"(\d+)"\)/);
  3670.             if(context.sniffRedir(posli + sm[1] + "&id=" + sm[2], callback))
  3671.               context.start();
  3672.           }
  3673.       });
  3674.       
  3675.       var processedBy = arguments.callee.name;
  3676.       function callback(url) {
  3677.         if (url) context.change(l, url, processedBy, true)
  3678.         context.done();
  3679.       }
  3680.     },
  3681.     
  3682.     function linkbucks_com(l, context) {
  3683.       if (/^http:\/\/(?:[\w-]+\.)linkbucks\.com\/link\/[^?]*$/.test(l.href)) {
  3684.         context.load(l.href, function(req) {
  3685.           var m = req.responseText.match(/document\.location\.href\s*=\s*"(https?:\/\/[^"]*)/);
  3686.           if (m) context.change(l, m[1]);
  3687.         });
  3688.       }
  3689.     },
  3690.     
  3691.     
  3692.     function megaupload_com(l, context, redirected) {
  3693.       if (context.megauploadDirect ||
  3694.           !/^http:\/\/(?:[\w-]+\.)?mega(?:upload|rotic)\.com\/.*\?/.test(l.href)
  3695.         ) return;
  3696.  
  3697.       if(!redirected) {
  3698.         if(!context.megauploadDequeing) {
  3699.           if (context.megauploadQueue) {
  3700.             context.megauploadQueue.push(l);
  3701.             return;
  3702.           }
  3703.           context.megauploadQueue = [];
  3704.         }
  3705.         
  3706.         var processor = arguments.callee;
  3707.         if (context.sniffRedir(l.href, function(url) {
  3708.            try {
  3709.              if (/\/files?\//.test(url)
  3710.                 ) { // direct link active
  3711.                if (!context.prefs.getBoolPref("megaupload_com.force")) {
  3712.                  context.megauploadDirect = true;
  3713.                  context.megauploadQueue = null;
  3714.                  return;
  3715.                }
  3716.                l.href = url; 
  3717.              }
  3718.              processor(l, context, true);
  3719.            } finally {
  3720.              try {
  3721.                if (context.megauploadQueue) {
  3722.                  context.megauploadDequeing = true;
  3723.                  for each(l in context.megauploadQueue) {
  3724.                    context.start();
  3725.                    try {
  3726.                      processor(l, context);
  3727.                    } finally {
  3728.                      context.done();
  3729.                    }
  3730.                  }
  3731.                }
  3732.              } finally {
  3733.                context.megauploadQueue = null;
  3734.                context.done();
  3735.              }
  3736.            }
  3737.           })) {
  3738.           context.start();
  3739.         }
  3740.         return;
  3741.       }
  3742.       
  3743.       context.load(l.href, function(req) {
  3744.         var m = req.responseText.match(
  3745.               /var\s*(\w)\s*=.*?abs.*?(\d+).*\n\s*var\s*(\w)\s*=\s*'([^']*)'.*?sqrt.*?(\d+).*\n.*?"(http.*?)'\s*\+\s*\3\s*\+\s*\1\s*\+\s*'(.*?)"/
  3746.               );
  3747.         if (m) {
  3748.           var x = String.fromCharCode(Math.abs(m[2]));
  3749.           var y = m[4] + String.fromCharCode(Math.sqrt(m[5]));
  3750.           var url = m[6] + y + x + m[7];
  3751.           context.change(l, url);
  3752.         } else {
  3753.           m = req.responseText.match(/<div id="servererror">[\s\S]*?<td[^>]*>([^<]*)/i, req.responseText);
  3754.           l.description = m && m[1] || "ERROR"; 
  3755.         }
  3756.       });
  3757.     },
  3758.     
  3759.     function rapidbolt_com(l, context) {
  3760.       if (/^https?:\/\/(?:[^\/]*\w\.)?rapidbolt\.com\//i.test(l.href))
  3761.         context.load(l.href, function(req) {
  3762.             var m =req.responseText.match(/https?:\/\/.*?rapidshare\.com[^"'\s]*/);
  3763.             if (m) context.change(l, m[0]);
  3764.         });
  3765.     },
  3766.     
  3767.     function rsprotect_com(l, context) {
  3768.       if (/^https?:\/\/(?:[^\/]*\w\.)?rsprotect\.com\//i.test(l.href))
  3769.         context.load(l.href, function(req) {
  3770.             var m = req.responseText.match(/\baction\s*=["'\s]*?(https?:\/\/.*?rapidshare\.com[^"'\s]*)/i);
  3771.             if (m) context.change(l, m[1].replace(/&#x([0-9a-f]+);/ig, 
  3772.                 function($, $1) { return String.fromCharCode(parseInt("0x" + $1))}));
  3773.         });
  3774.     },
  3775.  
  3776.     function stealth_to(l, context) {
  3777.  
  3778.       var doc = context.links.document;
  3779.       if(!doc) return;
  3780.  
  3781.       var rx = /http:\/\/stealth\.to\/.*[&\?]id=/;
  3782.       if (!(rx.test(l.href) || 
  3783.           !context.stealth_to_topChecked && doc && rx.test(doc.URL))) 
  3784.         return;
  3785.       var stealth_to = arguments.callee;
  3786.       if(!context.stealth_to_topChecked) {
  3787.          context.stealth_to_topChecked = true;
  3788.          if(doc && rx.test(doc.URL) && 
  3789.             checkList(doc.documentElement.innerHTML)) 
  3790.            return;
  3791.       }
  3792.       
  3793.       var postData = context.links.postData || l.href.match(/&code=.*/) || null;
  3794.       if (postData) {
  3795.         l.href = l.href.replace(/&code=.*/, '');
  3796.         postData = postData.toString();
  3797.       }
  3798.      
  3799.       context.load(l.href, function(req) {
  3800.           checkAll(req.responseText);
  3801.         }, postData);
  3802.       
  3803.       function checkAll(html) {
  3804.         return checkCaptcha(html) || checkList(html) || checkAjax(html);
  3805.       }
  3806.     
  3807.       function checkCaptcha(html) {
  3808.         if (/<input[^>]*code/.test(html)) { // captcha page
  3809.           var docURL = l.href + "#FlashGot_Form";
  3810.           var ee, j, f;
  3811.           var renew = null;
  3812.           if(docURL == doc.URL) {
  3813.            renew = doc;
  3814.           } else {
  3815.             ee = doc.getElementsByTagName("iframe");
  3816.           
  3817.             for(j = ee.length; j-- > 0;) 
  3818.               if(ee[j].src == docURL) break;
  3819.             
  3820.             if(j >= 0) {
  3821.              f = ee[j];
  3822.              renew = f.contentDocument;
  3823.             } else {
  3824.               ee = doc.getElementsByTagName("a");
  3825.               for(var j = ee.length; j-- > 0;)
  3826.                 if(ee[j].href == l.href) break;
  3827.        
  3828.               var a = j < 0 ? doc.body : ee[j];
  3829.               var f = doc.createElement("iframe");
  3830.               f.style.display = "block";
  3831.               f.style.width="350px";
  3832.               f.style.height="120px";
  3833.               f.style.borderStyle = "solid";
  3834.               f.style.borderColor = "orange";
  3835.               f.style.borderWidth = "2px";
  3836.               doc.defaultView.addEventListener("DOMFrameContentLoaded", function(ev) {
  3837.               var d = ev.target.contentDocument;
  3838.               if (!d.body) return;
  3839.               d.body.removeAttribute("onload");
  3840.               
  3841.               var f = d.getElementsByTagName("form")[0];
  3842.               d.body.insertBefore(f, d.body.firstChild);
  3843.               var ii = d.getElementsByTagName("img");
  3844.               for (var j = ii.length; j-- > 0;) {
  3845.                 if (/captcha/.test(ii[j].src)) {
  3846.                   d.body.insertBefore(ii[j], f).style.display="block";
  3847.                   break;
  3848.                 }
  3849.               }
  3850.               
  3851.               ii = d.getElementsByTagName("input");
  3852.               for(j = 0; j < ii.length; j++) f.appendChild(ii[j]);
  3853.               while(f.nextSibling) d.body.removeChild(f.nextSibling);
  3854.               ii = d.getElementsByTagName("link");
  3855.               for(j = 0; j < ii.length; j++) ii[j].href="data:";
  3856.               }, true);
  3857.               f.src = docURL;
  3858.               a.appendChild(f);
  3859.             }
  3860.           }
  3861.           
  3862.           if(renew) {
  3863.             // renew captcha
  3864.             ee = renew.getElementsByTagName("img");
  3865.              for(j = ee.length; j-- > 0;) 
  3866.                ee[j].src = ee[j].src + "?" + new Date().getTime();
  3867.           }
  3868.           
  3869.           context.links.splice(context.links.indexOf(l), 1); 
  3870.           return true;
  3871.         }
  3872.         return false;
  3873.       }
  3874.       
  3875.       function checkList(html) {
  3876.         var m = html.match(/\bdownload\(['"]?\d+/g);
  3877.         if (m) {
  3878.           
  3879.           var nl, args, p;
  3880.           var ids = [];
  3881.           args = [ context.links.indexOf(l), 1 ]; // Array.splice parameters
  3882.           for each (var id in m) {
  3883.             id = id.replace(/\D/g, '');
  3884.             if (ids.indexOf(id) > -1) continue;
  3885.             ids.push(id);
  3886.             // copy link;
  3887.             nl = {};
  3888.             for(p in l) nl[p] = l[p];
  3889.             nl.href = "http://stealth.to/index.php?go=download&id=" + id;  
  3890.             stealth_to(nl, context);
  3891.             args.push(nl); 
  3892.           }
  3893.           Array.prototype.splice.apply(context.links, args); // replace parent link with children
  3894.           if(/#FlashGot_Form$/.test(doc.URL))
  3895.           {
  3896.             // close iframe
  3897.             var ee = doc.defaultView.parent.document.getElementsByTagName("iframe");
  3898.             for(var j = ee.length; j-- > 0;) {
  3899.               if(ee[j].contentDocument == doc) {
  3900.                 ee[j].parentNode.removeChild(ee[j]);
  3901.               }
  3902.             }
  3903.           }
  3904.           return true;
  3905.         }
  3906.         return false;
  3907.       }
  3908.       
  3909.       function checkAjax(html) {
  3910.         var parts = html.split("|||");
  3911.         if (parts.length < 2) return false;
  3912.         context.change(l, "http://" + parts[0]);
  3913.         return true;
  3914.       }
  3915.       
  3916.     },
  3917.     
  3918.     function shorten_ws(l, context) {
  3919.       if (/^http:\/\/[^\/]*shorten\.ws\//.test(l.href))
  3920.         context.load(l.href, function(req) {
  3921.           var m = req.responseText.match(/<table[^>]*shortURLTable[\s\S]*?<a[^>]*(https?:[^ ">]*)/);
  3922.           if (m) context.change(l, m[1]);
  3923.         });
  3924.     },
  3925.     
  3926.     function tube_url(l, context) {
  3927.       if (/^http:\/\/(?:[^\/]+\.)*tubeurl\.com\//.test(l.href))
  3928.         context.load(l.href, function(req) {
  3929.           var m = req.responseText.match(/<meta[^>]*refresh[^>]*(https?:[^ ">]*)/i);
  3930.           if (m) context.change(l, m[1]);
  3931.         });
  3932.     },
  3933.     
  3934.     function tinyurl_com(l, context) { // tinyurl.com or moourl.com or downloads.sourceforge.net
  3935.       var m = l.href.match(/^https?:\/\/(?:(tiny|moo)url\.com|(?:[^\/\.]+\.)?sourceforge\.net)\//);
  3936.       if (!m) return;
  3937.       var processedBy = m[1] ? m[1] + "url_com" : "sf_net";
  3938.       var limit = processedBy == "sf_net" ? 0 : 20;
  3939.       var method = processedBy == "moourl_com" ? "GET" : "HEAD";
  3940.       var callback = function(url) {
  3941.         if (url) context.change(l, url, processedBy);
  3942.         context.done();
  3943.       };
  3944.       if (context.sniffRedir(l.href, callback, method,  limit)) {
  3945.         context.start();
  3946.       }
  3947.     },
  3948.     
  3949.     function zshare_net(l, context) {
  3950.       if (/^http:\/\/(?:[^\/]+\.)*zshare\.net\/[a-z]+\/[a-z0-9]+\/$/.test(l.href)) {
  3951.         context.load(l.href.replace(/^(http:\/\/(?:[^\/]+\.)*zshare\.net\/)[a-z]+(\/.*)/, '$1download$2'),
  3952.         function(req) {
  3953.           var m = req.responseText.match(/Array\('([\s\S]*?)'\)/);
  3954.           if (m) context.change(l, m[1].split(/'\s*,\s*'/).join(''));
  3955.         }, "download=1&imageField=");
  3956.       }
  3957.     },
  3958.     
  3959.     function media(l, context) {
  3960.       if (!l.contentType) return;
  3961.       switch(l.contentType) {
  3962.         // see http://gonze.com/playlists/playlist-format-survey.html
  3963.         case "audio/mpegurl":
  3964.         case "audio/x-mpegurl":
  3965.         // case "video/x-ms-asf": // we should need to differentiate asx from asf, see MediaSniffer
  3966.         case "video/x-ms-asx":
  3967.         case "video/x-ms-wax":
  3968.         case "video/x-ms-wvx":
  3969.         case "audio/vnd.rn-realaudio":
  3970.         case "audio/x-pn-realaudio":
  3971.         case "application/smil":
  3972.         case "audio/x-scpls":
  3973.         break;
  3974.         default:
  3975.         return;
  3976.       }
  3977.       context.load(l.href,
  3978.         function(req) {
  3979.           var urls = req.responseText.match(/\b[a-z]{3,6}:\/\/[^\s<"']*/g);
  3980.           if (!urls) return;
  3981.           for each(var u in urls) {
  3982.             context.change(l, u, "media", true);
  3983.           }
  3984.         });
  3985.     },
  3986.     
  3987.     function generic(l, context) {
  3988.       if (l.contentType) return; // avoid jamming FlashGot Media
  3989.       
  3990.       if (typeof(context.genericRx) != "object") {
  3991.         try {
  3992.           context.genericRx = new RegExp(context.dm.service.getPref("redir.generic.rx", null), "i");
  3993.         } catch(e) {
  3994.           context.genericRx = null;
  3995.         }
  3996.       }  
  3997.       if (context.genericRx == null) return;
  3998.       var m = l.href.match(context.genericRx);
  3999.       if (m) {
  4000.         var href = m[1];
  4001.         context.change(l, /^https?%3a/i.test(href) ? unescape(href) : href, null, true); // latest arg -> add, rather than replace
  4002.       }
  4003.     },
  4004.  
  4005.   ],
  4006.   
  4007.   sniffRedir: function(url, callback, method, limit) {
  4008.     var ch = CC["@mozilla.org/network/io-service;1"].getService(CI.nsIIOService
  4009.         ).newChannel(url, null, null);
  4010.     if(!(ch instanceof CI.nsIHttpChannel)) return false;
  4011.     ch.requestMethod = method || "HEAD";
  4012.     ch.redirectionLimit = typeof(limit) == "undefined" ? 20 : limit;
  4013.     ch.asyncOpen(this.redirSniffer, {
  4014.        callback: callback,
  4015.        get wrappedJSObject() { return this; }
  4016.     });
  4017.     return true;
  4018.   },
  4019.   redirSniffer: {
  4020.     onStartRequest: function(req, ctx) {
  4021.       req.cancel(NS_BINDING_ABORTED);
  4022.     },
  4023.     onDataAvailable: function(req, ctx , stream , offset , count ) {},
  4024.     onStopRequest: function(req, ctx) {
  4025.       var url;
  4026.       if (req instanceof CI.nsIHttpChannel) {
  4027.         try {
  4028.           url = req.URI.resolve(req.getResponseHeader("Location"));
  4029.         } catch(e) {}
  4030.       }
  4031.       if (!url) {
  4032.         url = (req instanceof CI.nsIChannel) ? req.URI.spec : "";
  4033.       }
  4034.       ctx.wrappedJSObject.callback(url);
  4035.     }
  4036.   }
  4037.   
  4038. };
  4039.  
  4040.  
  4041. // XPCOM Scaffolding code
  4042. const EXTENSION_ID = "{19503e42-ca3c-4c27-b1e2-9cdb2170ee34}";
  4043.  
  4044. // component defined in this file
  4045.  
  4046. const SERVICE_NAME="FlashGot Service";
  4047. const SERVICE_CID = Components.ID("{2a55fc5c-7b31-4ee1-ab15-5ee2eb428cbe}");
  4048. const SERVICE_CTRID = "@maone.net/flashgot-service;1";
  4049.     
  4050. const SERVICE_CONSTRUCTOR=FlashGotService;
  4051. const SERVICE_FLAGS = 3; // SINGLETON | THREADSAFE
  4052.  
  4053. // interfaces implemented by this component
  4054. const SERVICE_IIDS = 
  4055. CI.nsISupports,
  4056. CI.nsISupportsWeakReference,
  4057. CI.nsIClassInfo,
  4058. CI.nsIObserver,
  4059. CI.nsIURIContentListener
  4060. ];
  4061.  
  4062. // Factory object
  4063. const SERVICE_FACTORY = {
  4064.   _instance: null,
  4065.   createInstance: function (outer, iid) {
  4066.     if (outer != null)
  4067.         throw Components.results.NS_ERROR_NO_AGGREGATION;
  4068.  
  4069.     xpcom_checkInterfaces(iid,SERVICE_IIDS,Components.results.NS_ERROR_INVALID_ARG);
  4070.     // kept this for flexibility sake, but we're really adopting an
  4071.     // early instantiation and late init singleton pattern
  4072.     return this._instance==null?this._instance=this._create():this._instance;
  4073.   },
  4074.   _create: function() {
  4075.     var obj=new SERVICE_CONSTRUCTOR();
  4076.     obj.__defineGetter__("classDescription",function() { return SERVICE_NAME; });
  4077.     obj.__defineGetter__("classID",function() { return SERVICE_CID; });
  4078.     obj.__defineGetter__("classIDNoAlloc",function() { return SERVICE_CTRID; });
  4079.     obj.__defineGetter__("contractID",function() { return SERVICE_CTRID; });
  4080.     obj.__defineGetter__("flags",function() { return SERVICE_FLAGS; });
  4081.     obj.__defineGetter__("implementationLanguage",function() { return 2; });
  4082.     obj.getHelperForLanguage = function() { return null; };
  4083.     obj.getInterfaces = function(count) { 
  4084.       count.value = 0; 
  4085.       return null; 
  4086.     };
  4087.     return obj;
  4088.   }
  4089. };
  4090.  
  4091. function xpcom_checkInterfaces(iid,iids,ex) {
  4092.   for (var j=iids.length; j-- >0;) {
  4093.     if (iid.equals(iids[j])) return true;
  4094.   }
  4095.   throw ex;
  4096. }
  4097.  
  4098. // Module
  4099.  
  4100. var Module = new Object();
  4101. Module.firstTime=true;
  4102. Module.registerSelf = function (compMgr, fileSpec, location, type) {
  4103.   if (this.firstTime) {
  4104.    
  4105.     debug("*** Registering "+SERVICE_CTRID+".\n");
  4106.     SERVICE_CONSTRUCTOR.prototype.fileSpec = fileSpec;
  4107.  
  4108.     compMgr.QueryInterface(CI.nsIComponentRegistrar
  4109.       ).registerFactoryLocation(SERVICE_CID,
  4110.       SERVICE_NAME,
  4111.       SERVICE_CTRID, 
  4112.       fileSpec,
  4113.       location, 
  4114.       type);
  4115.       
  4116.     CC['@mozilla.org/categorymanager;1'].getService(
  4117.       CI.nsICategoryManager
  4118.      ).addCategoryEntry("app-startup",
  4119.         SERVICE_NAME, "service," + SERVICE_CTRID, true, true, null);
  4120.       
  4121.     this.firstTime=false;
  4122.   } 
  4123. }
  4124. Module.unregisterSelf = function(compMgr, fileSpec, location) {
  4125.   compMgr.QueryInterface(CI.nsIComponentRegistrar
  4126.     ).unregisterFactoryLocation(SERVICE_CID, fileSpec);
  4127.   CC['@mozilla.org/categorymanager;1'].getService(
  4128.       CI.nsICategoryManager
  4129.      ).deleteCategoryEntry("app-startup",SERVICE_NAME, true);
  4130. }
  4131.  
  4132. Module.getClassObject = function (compMgr, cid, iid) {
  4133.   if (cid.equals(SERVICE_CID))
  4134.     return SERVICE_FACTORY;
  4135.  
  4136.   if (!iid.equals(CI.nsIFactory))
  4137.     throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  4138.   
  4139.   throw Components.results.NS_ERROR_NO_INTERFACE;
  4140.     
  4141. }
  4142.  
  4143. Module.canUnload = function(compMgr) {
  4144.   return true;
  4145. }
  4146.  
  4147. SERVICE_CONSTRUCTOR.prototype.__defineGetter__("home", function()
  4148. {
  4149.   var f = null;
  4150.   try {
  4151.     f = CC["@mozilla.org/extensions/manager;1"].
  4152.               getService(CI.nsIExtensionManager)
  4153.               .getInstallLocation(EXTENSION_ID)
  4154.               .getItemLocation(EXTENSION_ID);
  4155.     f.append("components");
  4156.   } catch(e) {
  4157.     try {
  4158.       var prefs = CC["@mozilla.org/preferences-service;1"].getService(CI.nsIPrefBranch)
  4159.       if (this.fileSpec && (this.fileSpec instanceof CI.nsILocalFile)) {
  4160.         prefs.setComplexValue("extensions." + EXTENSION_ID + ".home", CI.nsILocalFile,
  4161.                 this.fileSpec.parent);
  4162.       }
  4163.       f = CC["@mozilla.org/preferences-service;1"].getService(CI.nsIPrefBranch)
  4164.           .getComplexValue("extensions." + EXTENSION_ID + ".home", CI.nsILocalFile);
  4165.     } catch(e2) {
  4166.       dump(e2);
  4167.       f = null;
  4168.     }
  4169.   }
  4170.   this.__defineGetter__("home", function() { return f; });
  4171.   return f;
  4172. });
  4173.  
  4174. // entrypoint
  4175. function NSGetModule(compMgr, fileSpec) {
  4176.   return Module;
  4177. }
  4178.  
  4179.